aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/thermal.c3
-rw-r--r--drivers/amba/bus.c13
-rw-r--r--drivers/ata/pata_sis.c10
-rw-r--r--drivers/atm/ambassador.c2
-rw-r--r--drivers/atm/atmtcp.c6
-rw-r--r--drivers/atm/eni.c4
-rw-r--r--drivers/atm/eni.h2
-rw-r--r--drivers/atm/fore200e.c20
-rw-r--r--drivers/atm/fore200e.h2
-rw-r--r--drivers/atm/he.c4
-rw-r--r--drivers/atm/idt77252.c28
-rw-r--r--drivers/atm/nicstar.c14
-rw-r--r--drivers/base/attribute_container.c26
-rw-r--r--drivers/base/base.h2
-rw-r--r--drivers/base/bus.c112
-rw-r--r--drivers/base/class.c2
-rw-r--r--drivers/base/core.c293
-rw-r--r--drivers/base/dd.c66
-rw-r--r--drivers/base/dmapool.c14
-rw-r--r--drivers/base/driver.c20
-rw-r--r--drivers/base/firmware_class.c10
-rw-r--r--drivers/base/power/main.c3
-rw-r--r--drivers/base/power/resume.c13
-rw-r--r--drivers/base/power/shutdown.c2
-rw-r--r--drivers/base/power/suspend.c12
-rw-r--r--drivers/block/aoe/aoe.h9
-rw-r--r--drivers/block/aoe/aoecmd.c17
-rw-r--r--drivers/block/aoe/aoenet.c2
-rw-r--r--drivers/block/cciss.c35
-rw-r--r--drivers/block/paride/pcd.c2
-rw-r--r--drivers/block/paride/pf.c2
-rw-r--r--drivers/block/pktcdvd.c3
-rw-r--r--drivers/block/ub.c11
-rw-r--r--drivers/bluetooth/bfusb.c2
-rw-r--r--drivers/bluetooth/bluecard_cs.c6
-rw-r--r--drivers/bluetooth/bpa10x.c4
-rw-r--r--drivers/bluetooth/bt3c_cs.c6
-rw-r--r--drivers/bluetooth/btuart_cs.c6
-rw-r--r--drivers/bluetooth/dtl1_cs.c2
-rw-r--r--drivers/bluetooth/hci_h4.c6
-rw-r--r--drivers/char/mem.c2
-rw-r--r--drivers/char/mxser.c48
-rw-r--r--drivers/char/mxser_new.c45
-rw-r--r--drivers/char/pcmcia/synclink_cs.c2
-rw-r--r--drivers/char/random.c38
-rw-r--r--drivers/connector/connector.c4
-rw-r--r--drivers/hwmon/w83627ehf.c20
-rw-r--r--drivers/i2c/busses/Kconfig3
-rw-r--r--drivers/i2c/busses/i2c-pasemi.c6
-rw-r--r--drivers/ide/Kconfig1
-rw-r--r--drivers/ide/ide-proc.c4
-rw-r--r--drivers/ide/pci/delkin_cb.c1
-rw-r--r--drivers/ide/pci/hpt366.c5
-rw-r--r--drivers/ieee1394/eth1394.c4
-rw-r--r--drivers/ieee1394/eth1394.h2
-rw-r--r--drivers/ieee1394/nodemgr.c22
-rw-r--r--drivers/infiniband/core/mad.c34
-rw-r--r--drivers/infiniband/core/sa_query.c24
-rw-r--r--drivers/infiniband/core/smi.c86
-rw-r--r--drivers/infiniband/core/smi.h34
-rw-r--r--drivers/infiniband/core/sysfs.c1
-rw-r--r--drivers/infiniband/core/ucm.c23
-rw-r--r--drivers/infiniband/core/ucma.c22
-rw-r--r--drivers/infiniband/core/user_mad.c20
-rw-r--r--drivers/infiniband/hw/amso1100/c2.c6
-rw-r--r--drivers/infiniband/hw/amso1100/c2_provider.c1
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cm.c29
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c1
-rw-r--r--drivers/infiniband/hw/ehca/ehca_classes.h1
-rw-r--r--drivers/infiniband/hw/ehca/ehca_hca.c55
-rw-r--r--drivers/infiniband/hw/ehca/ehca_main.c1
-rw-r--r--drivers/infiniband/hw/ehca/hcp_if.c24
-rw-r--r--drivers/infiniband/hw/ehca/hcp_if.h4
-rw-r--r--drivers/infiniband/hw/ipath/ipath_common.h23
-rw-r--r--drivers/infiniband/hw/ipath/ipath_cq.c38
-rw-r--r--drivers/infiniband/hw/ipath/ipath_debug.h1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_diag.c11
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c123
-rw-r--r--drivers/infiniband/hw/ipath/ipath_eeprom.c4
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c287
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6110.c152
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6120.c73
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c86
-rw-r--r--drivers/infiniband/hw/ipath/ipath_intr.c100
-rw-r--r--drivers/infiniband/hw/ipath/ipath_kernel.h10
-rw-r--r--drivers/infiniband/hw/ipath/ipath_keys.c14
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mr.c12
-rw-r--r--drivers/infiniband/hw/ipath/ipath_qp.c133
-rw-r--r--drivers/infiniband/hw/ipath/ipath_rc.c920
-rw-r--r--drivers/infiniband/hw/ipath/ipath_registers.h22
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ruc.c63
-rw-r--r--drivers/infiniband/hw/ipath/ipath_stats.c16
-rw-r--r--drivers/infiniband/hw/ipath/ipath_uc.c6
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ud.c8
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c15
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.h57
-rw-r--r--drivers/infiniband/hw/mthca/mthca_main.c10
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mr.c7
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c1
-rw-r--r--drivers/infiniband/hw/mthca/mthca_qp.c7
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c66
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c10
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c12
-rw-r--r--drivers/input/gameport/gameport.c39
-rw-r--r--drivers/input/serio/serio.c41
-rw-r--r--drivers/isdn/act2000/module.c2
-rw-r--r--drivers/isdn/gigaset/usb-gigaset.c2
-rw-r--r--drivers/isdn/hardware/avm/b1dma.c3
-rw-r--r--drivers/isdn/hardware/avm/c4.c3
-rw-r--r--drivers/isdn/hisax/elsa_ser.c6
-rw-r--r--drivers/isdn/hisax/isdnl2.c3
-rw-r--r--drivers/isdn/hysdn/hycapi.c5
-rw-r--r--drivers/isdn/hysdn/hysdn_net.c2
-rw-r--r--drivers/isdn/hysdn/hysdn_sched.c5
-rw-r--r--drivers/isdn/i4l/isdn_common.c2
-rw-r--r--drivers/isdn/i4l/isdn_net.c11
-rw-r--r--drivers/isdn/i4l/isdn_ppp.c9
-rw-r--r--drivers/isdn/isdnloop/isdnloop.c3
-rw-r--r--drivers/isdn/pcbit/capi.c12
-rw-r--r--drivers/kvm/mmu.c1
-rw-r--r--drivers/macintosh/smu.c4
-rw-r--r--drivers/md/bitmap.c4
-rw-r--r--drivers/media/common/ir-keymaps.c18
-rw-r--r--drivers/media/common/saa7146_video.c6
-rw-r--r--drivers/media/dvb/b2c2/Kconfig1
-rw-r--r--drivers/media/dvb/b2c2/flexcop-fe-tuner.c3
-rw-r--r--drivers/media/dvb/b2c2/flexcop-pci.c9
-rw-r--r--drivers/media/dvb/bt8xx/Kconfig2
-rw-r--r--drivers/media/dvb/bt8xx/bt878.c4
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.c3
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.h2
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.c56
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.h2
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c20
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c48
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.h1
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.c1
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h1
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig12
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile4
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.c6
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c4
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c4
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h6
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-remote.c2
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.c8
-rw-r--r--drivers/media/dvb/dvb-usb/m920x.c235
-rw-r--r--drivers/media/dvb/dvb-usb/m920x.h46
-rw-r--r--drivers/media/dvb/dvb-usb/opera1.c581
-rw-r--r--drivers/media/dvb/dvb-usb/opera1.h9
-rw-r--r--drivers/media/dvb/dvb-usb/ttusb2.c7
-rw-r--r--drivers/media/dvb/frontends/Kconfig28
-rw-r--r--drivers/media/dvb/frontends/Makefile3
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c340
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h16
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.c4
-rw-r--r--drivers/media/dvb/frontends/lgh06xf.c134
-rw-r--r--drivers/media/dvb/frontends/lgh06xf.h35
-rw-r--r--drivers/media/dvb/frontends/or51132.c305
-rw-r--r--drivers/media/dvb/frontends/tda10021.c47
-rw-r--r--drivers/media/dvb/frontends/tda10023.c540
-rw-r--r--drivers/media/dvb/frontends/tda1002x.h (renamed from drivers/media/dvb/frontends/tda10021.h)33
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c98
-rw-r--r--drivers/media/dvb/frontends/tda1004x.h54
-rw-r--r--drivers/media/dvb/frontends/tda827x.c512
-rw-r--r--drivers/media/dvb/frontends/tda827x.h62
-rw-r--r--drivers/media/dvb/pluto2/Kconfig1
-rw-r--r--drivers/media/dvb/ttpci/Kconfig6
-rw-r--r--drivers/media/dvb/ttpci/av7110.c17
-rw-r--r--drivers/media/dvb/ttpci/av7110.h28
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.c24
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.h10
-rw-r--r--drivers/media/dvb/ttpci/av7110_ir.c365
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c147
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c96
-rw-r--r--drivers/media/dvb/ttpci/budget-core.c58
-rw-r--r--drivers/media/dvb/ttpci/budget.h3
-rw-r--r--drivers/media/dvb/ttusb-budget/Kconfig1
-rw-r--r--drivers/media/radio/radio-aimslab.c240
-rw-r--r--drivers/media/radio/radio-gemtek-pci.c253
-rw-r--r--drivers/media/radio/radio-gemtek.c260
-rw-r--r--drivers/media/radio/radio-maestro.c266
-rw-r--r--drivers/media/radio/radio-rtrack2.c255
-rw-r--r--drivers/media/radio/radio-sf16fmi.c262
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c350
-rw-r--r--drivers/media/radio/radio-terratec.c247
-rw-r--r--drivers/media/radio/radio-trust.c256
-rw-r--r--drivers/media/radio/radio-typhoon.c239
-rw-r--r--drivers/media/radio/radio-zoltrix.c256
-rw-r--r--drivers/media/video/Kconfig14
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c53
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c24
-rw-r--r--drivers/media/video/bt8xx/bttv-gpio.c5
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c2
-rw-r--r--drivers/media/video/bt8xx/bttv-if.c48
-rw-r--r--drivers/media/video/bt8xx/bttv.h25
-rw-r--r--drivers/media/video/bt8xx/bttvp.h3
-rw-r--r--drivers/media/video/cafe_ccic.c79
-rw-r--r--drivers/media/video/cpia_pp.c49
-rw-r--r--drivers/media/video/cs53l32a.c4
-rw-r--r--drivers/media/video/cx2341x.c72
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c9
-rw-r--r--drivers/media/video/cx25840/cx25840-core.h3
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c1
-rw-r--r--drivers/media/video/cx88/Kconfig1
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c9
-rw-r--r--drivers/media/video/cx88/cx88-cards.c37
-rw-r--r--drivers/media/video/cx88/cx88-core.c8
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c31
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c3
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c29
-rw-r--r--drivers/media/video/cx88/cx88-video.c5
-rw-r--r--drivers/media/video/cx88/cx88.h4
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c2
-rw-r--r--drivers/media/video/ir-kbd-i2c.c2
-rw-r--r--drivers/media/video/ivtv/Kconfig26
-rw-r--r--drivers/media/video/ivtv/Makefile7
-rw-r--r--drivers/media/video/ivtv/ivtv-audio.c74
-rw-r--r--drivers/media/video/ivtv/ivtv-audio.h23
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c964
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.h207
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c303
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.h21
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c1374
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h868
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c921
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.h44
-rw-r--r--drivers/media/video/ivtv/ivtv-firmware.c272
-rw-r--r--drivers/media/video/ivtv/ivtv-firmware.h25
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c307
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.h25
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c748
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.h36
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c1567
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.h28
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.c838
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.h26
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.c360
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.h25
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.c262
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.h64
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c977
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.h31
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.c200
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.h43
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.c538
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.h26
-rw-r--r--drivers/media/video/ivtv/ivtv-version.h26
-rw-r--r--drivers/media/video/ivtv/ivtv-video.c142
-rw-r--r--drivers/media/video/ivtv/ivtv-video.h24
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.c1129
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.h24
-rw-r--r--drivers/media/video/msp3400-driver.c5
-rw-r--r--drivers/media/video/msp3400-driver.h1
-rw-r--r--drivers/media/video/ov7670.c40
-rw-r--r--drivers/media/video/planb.c6
-rw-r--r--drivers/media/video/planb.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.c2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c16
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h3
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c30
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c2
-rw-r--r--drivers/media/video/pwc/pwc-ctrl.c61
-rw-r--r--drivers/media/video/pwc/pwc-if.c16
-rw-r--r--drivers/media/video/pwc/pwc-ioctl.h36
-rw-r--r--drivers/media/video/pwc/pwc-kiara.c2
-rw-r--r--drivers/media/video/pwc/pwc-kiara.h5
-rw-r--r--drivers/media/video/pwc/pwc-timon.c4
-rw-r--r--drivers/media/video/pwc/pwc-timon.h6
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c60
-rw-r--r--drivers/media/video/pwc/pwc.h5
-rw-r--r--drivers/media/video/saa7115.c10
-rw-r--r--drivers/media/video/saa7127.c12
-rw-r--r--drivers/media/video/saa7134/Kconfig1
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c204
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c125
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c1027
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c4
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c1
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c85
-rw-r--r--drivers/media/video/saa7134/saa7134.h10
-rw-r--r--drivers/media/video/se401.c36
-rw-r--r--drivers/media/video/sn9c102/Kconfig2
-rw-r--r--drivers/media/video/sn9c102/Makefile17
-rw-r--r--drivers/media/video/sn9c102/sn9c102.h17
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c274
-rw-r--r--drivers/media/video/sn9c102/sn9c102_devtable.h14
-rw-r--r--drivers/media/video/sn9c102/sn9c102_hv7131d.c25
-rw-r--r--drivers/media/video/sn9c102/sn9c102_hv7131r.c366
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mi0343.c130
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mi0360.c338
-rw-r--r--drivers/media/video/sn9c102/sn9c102_ov7630.c121
-rw-r--r--drivers/media/video/sn9c102/sn9c102_ov7660.c234
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas106b.c23
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas202bcb.c77
-rw-r--r--drivers/media/video/sn9c102/sn9c102_sensor.h12
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5110c1b.c18
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5110d.c118
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5130d1b.c19
-rw-r--r--drivers/media/video/tda7432.c1
-rw-r--r--drivers/media/video/tda8290.c144
-rw-r--r--drivers/media/video/tda9875.c1
-rw-r--r--drivers/media/video/tuner-core.c28
-rw-r--r--drivers/media/video/tvaudio.c5
-rw-r--r--drivers/media/video/tveeprom.c45
-rw-r--r--drivers/media/video/upd64031a.c4
-rw-r--r--drivers/media/video/upd64083.c5
-rw-r--r--drivers/media/video/usbvideo/usbvideo.c18
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.c1165
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.h66
-rw-r--r--drivers/media/video/usbvision/usbvision-core.c10
-rw-r--r--drivers/media/video/usbvision/usbvision-i2c.c104
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c173
-rw-r--r--drivers/media/video/usbvision/usbvision.h39
-rw-r--r--drivers/media/video/v4l2-common.c41
-rw-r--r--drivers/media/video/videocodec.c3
-rw-r--r--drivers/media/video/videodev.c40
-rw-r--r--drivers/media/video/wm8739.c4
-rw-r--r--drivers/media/video/wm8775.c4
-rw-r--r--drivers/media/video/zr364xx.c929
-rw-r--r--drivers/message/fusion/mptlan.c36
-rw-r--r--drivers/mmc/mmc_sysfs.c27
-rw-r--r--drivers/mtd/Kconfig30
-rw-r--r--drivers/mtd/Makefile2
-rw-r--r--drivers/mtd/chips/Kconfig12
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c93
-rw-r--r--drivers/mtd/chips/fwh_lock.h3
-rw-r--r--drivers/mtd/devices/Kconfig26
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/at91_dataflash26.c485
-rw-r--r--drivers/mtd/devices/block2mtd.c67
-rw-r--r--drivers/mtd/maps/Kconfig47
-rw-r--r--drivers/mtd/maps/Makefile2
-rw-r--r--drivers/mtd/maps/alchemy-flash.c9
-rw-r--r--drivers/mtd/maps/ck804xrom.c2
-rw-r--r--drivers/mtd/maps/plat-ram.c3
-rw-r--r--drivers/mtd/maps/pmcmsp-flash.c184
-rw-r--r--drivers/mtd/maps/pmcmsp-ramroot.c105
-rw-r--r--drivers/mtd/maps/sun_uflash.c4
-rw-r--r--drivers/mtd/mtd_blkdevs.c43
-rw-r--r--drivers/mtd/mtdchar.c2
-rw-r--r--drivers/mtd/nand/Kconfig63
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/cafe.c25
-rw-r--r--drivers/mtd/nand/cmx270_nand.c267
-rw-r--r--drivers/mtd/nand/nand_base.c8
-rw-r--r--drivers/mtd/nand/nand_ids.c4
-rw-r--r--drivers/mtd/nand/nandsim.c518
-rw-r--r--drivers/mtd/onenand/Kconfig15
-rw-r--r--drivers/mtd/onenand/onenand_base.c21
-rw-r--r--drivers/mtd/ubi/Kconfig58
-rw-r--r--drivers/mtd/ubi/Kconfig.debug104
-rw-r--r--drivers/mtd/ubi/Makefile7
-rw-r--r--drivers/mtd/ubi/build.c848
-rw-r--r--drivers/mtd/ubi/cdev.c722
-rw-r--r--drivers/mtd/ubi/debug.c224
-rw-r--r--drivers/mtd/ubi/debug.h161
-rw-r--r--drivers/mtd/ubi/eba.c1241
-rw-r--r--drivers/mtd/ubi/gluebi.c323
-rw-r--r--drivers/mtd/ubi/io.c1259
-rw-r--r--drivers/mtd/ubi/kapi.c575
-rw-r--r--drivers/mtd/ubi/misc.c105
-rw-r--r--drivers/mtd/ubi/scan.c1368
-rw-r--r--drivers/mtd/ubi/scan.h167
-rw-r--r--drivers/mtd/ubi/ubi.h535
-rw-r--r--drivers/mtd/ubi/upd.c348
-rw-r--r--drivers/mtd/ubi/vmt.c809
-rw-r--r--drivers/mtd/ubi/vtbl.c809
-rw-r--r--drivers/mtd/ubi/wl.c1671
-rw-r--r--drivers/net/3c501.c1
-rw-r--r--drivers/net/3c505.c3
-rw-r--r--drivers/net/3c507.c1
-rw-r--r--drivers/net/3c509.c1
-rw-r--r--drivers/net/3c515.c2
-rw-r--r--drivers/net/3c523.c3
-rw-r--r--drivers/net/3c527.c1
-rw-r--r--drivers/net/3c59x.c2
-rw-r--r--drivers/net/7990.c3
-rw-r--r--drivers/net/8139cp.c6
-rw-r--r--drivers/net/8139too.c7
-rw-r--r--drivers/net/82596.c1
-rw-r--r--drivers/net/Kconfig1
-rw-r--r--drivers/net/Makefile2
-rw-r--r--drivers/net/a2065.c3
-rw-r--r--drivers/net/acenic.c1
-rw-r--r--drivers/net/amd8111e.c4
-rw-r--r--drivers/net/appletalk/cops.c4
-rw-r--r--drivers/net/appletalk/ltpc.c15
-rw-r--r--drivers/net/arcnet/arc-rawmode.c2
-rw-r--r--drivers/net/arcnet/arcnet.c17
-rw-r--r--drivers/net/arcnet/capmode.c14
-rw-r--r--drivers/net/arcnet/rfc1051.c2
-rw-r--r--drivers/net/arcnet/rfc1201.c2
-rw-r--r--drivers/net/ariadne.c1
-rw-r--r--drivers/net/arm/am79c961a.c1
-rw-r--r--drivers/net/arm/at91_ether.c1
-rw-r--r--drivers/net/arm/ep93xx_eth.c1
-rw-r--r--drivers/net/arm/ether1.c1
-rw-r--r--drivers/net/arm/ether3.c1
-rw-r--r--drivers/net/at1700.c1
-rw-r--r--drivers/net/atari_bionet.c7
-rw-r--r--drivers/net/atari_pamsnet.c6
-rw-r--r--drivers/net/atarilance.c1
-rw-r--r--drivers/net/atl1/atl1_main.c33
-rw-r--r--drivers/net/atp.c1
-rw-r--r--drivers/net/au1000_eth.c3
-rw-r--r--drivers/net/b44.c8
-rw-r--r--drivers/net/bmac.c1
-rw-r--r--drivers/net/bnx2.c44
-rw-r--r--drivers/net/bnx2.h1
-rw-r--r--drivers/net/bonding/bond_3ad.c8
-rw-r--r--drivers/net/bonding/bond_alb.c36
-rw-r--r--drivers/net/bonding/bond_main.c9
-rw-r--r--drivers/net/cassini.c11
-rw-r--r--drivers/net/chelsio/sge.c39
-rw-r--r--drivers/net/cris/eth_v10.c4
-rw-r--r--drivers/net/cs89x0.c2
-rw-r--r--drivers/net/cxgb3/cxgb3_defs.h5
-rw-r--r--drivers/net/cxgb3/cxgb3_offload.c71
-rw-r--r--drivers/net/cxgb3/sge.c39
-rw-r--r--drivers/net/cxgb3/t3_hw.c18
-rw-r--r--drivers/net/de600.c1
-rw-r--r--drivers/net/de620.c1
-rw-r--r--drivers/net/declance.c1
-rw-r--r--drivers/net/defxx.c6
-rw-r--r--drivers/net/depca.c4
-rw-r--r--drivers/net/dgrs.c3
-rw-r--r--drivers/net/dl2k.c4
-rw-r--r--drivers/net/dm9000.c1
-rw-r--r--drivers/net/e100.c2
-rw-r--r--drivers/net/e1000/e1000_main.c176
-rw-r--r--drivers/net/eepro.c1
-rw-r--r--drivers/net/eepro100.c6
-rw-r--r--drivers/net/eexpress.c1
-rw-r--r--drivers/net/ehea/ehea_main.c39
-rw-r--r--drivers/net/epic100.c3
-rw-r--r--drivers/net/eth16i.c1
-rw-r--r--drivers/net/ewrk3.c1
-rw-r--r--drivers/net/fealnx.c1
-rw-r--r--drivers/net/fec.c1
-rw-r--r--drivers/net/fec_8xx/fec_main.c5
-rw-r--r--drivers/net/forcedeth.c30
-rw-r--r--drivers/net/fs_enet/fs_enet-main.c9
-rw-r--r--drivers/net/gianfar.c12
-rw-r--r--drivers/net/hamachi.c1
-rw-r--r--drivers/net/hamradio/baycom_ser_fdx.c6
-rw-r--r--drivers/net/hamradio/bpqether.c2
-rw-r--r--drivers/net/hamradio/dmascc.c2
-rw-r--r--drivers/net/hamradio/hdlcdrv.c4
-rw-r--r--drivers/net/hamradio/yam.c4
-rw-r--r--drivers/net/hp100.c1
-rw-r--r--drivers/net/ibm_emac/ibm_emac_core.c3
-rw-r--r--drivers/net/ibmlana.c1
-rw-r--r--drivers/net/ibmveth.c1
-rw-r--r--drivers/net/ioc3-eth.c13
-rw-r--r--drivers/net/irda/ali-ircc.c9
-rw-r--r--drivers/net/irda/au1k_ir.c6
-rw-r--r--drivers/net/irda/donauboe.c8
-rw-r--r--drivers/net/irda/irda-usb.c6
-rw-r--r--drivers/net/irda/mcs7780.c38
-rw-r--r--drivers/net/irda/nsc-ircc.c15
-rw-r--r--drivers/net/irda/pxaficp_ir.c6
-rw-r--r--drivers/net/irda/sa1100_ir.c2
-rw-r--r--drivers/net/irda/smsc-ircc2.c5
-rw-r--r--drivers/net/irda/stir4200.c5
-rw-r--r--drivers/net/irda/via-ircc.c18
-rw-r--r--drivers/net/irda/vlsi_ir.c4
-rw-r--r--drivers/net/irda/w83977af_ir.c12
-rw-r--r--drivers/net/iseries_veth.c1
-rw-r--r--drivers/net/ixgb/ixgb_main.c36
-rw-r--r--drivers/net/ixp2000/ixpdev.c3
-rw-r--r--drivers/net/lance.c3
-rw-r--r--drivers/net/lasi_82596.c1
-rw-r--r--drivers/net/lib8390.c1
-rw-r--r--drivers/net/loopback.c24
-rw-r--r--drivers/net/lp486e.c1
-rw-r--r--drivers/net/mac89x0.c1
-rw-r--r--drivers/net/macb.c11
-rw-r--r--drivers/net/mace.c1
-rw-r--r--drivers/net/macmace.c4
-rw-r--r--drivers/net/meth.c11
-rw-r--r--drivers/net/mipsnet.c1
-rw-r--r--drivers/net/mv643xx_eth.c9
-rw-r--r--drivers/net/myri10ge/myri10ge.c7
-rw-r--r--drivers/net/myri_sbus.c4
-rw-r--r--drivers/net/natsemi.c1
-rw-r--r--drivers/net/netx-eth.c1
-rw-r--r--drivers/net/netxen/netxen_nic_hw.c15
-rw-r--r--drivers/net/netxen/netxen_nic_init.c1
-rw-r--r--drivers/net/netxen/netxen_nic_main.c12
-rw-r--r--drivers/net/ni5010.c1
-rw-r--r--drivers/net/ni52.c3
-rw-r--r--drivers/net/ni65.c7
-rw-r--r--drivers/net/ns83820.c5
-rw-r--r--drivers/net/pasemi_mac.c14
-rw-r--r--drivers/net/pci-skeleton.c3
-rw-r--r--drivers/net/pcmcia/3c574_cs.c1
-rw-r--r--drivers/net/pcmcia/3c589_cs.c1
-rw-r--r--drivers/net/pcmcia/axnet_cs.c3
-rw-r--r--drivers/net/pcmcia/fmvj18x_cs.c1
-rw-r--r--drivers/net/pcmcia/nmclan_cs.c4
-rw-r--r--drivers/net/pcmcia/smc91c92_cs.c1
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c1
-rw-r--r--drivers/net/pcnet32.c1
-rw-r--r--drivers/net/phy/fixed.c6
-rw-r--r--drivers/net/phy/phy_device.c9
-rw-r--r--drivers/net/plip.c2
-rw-r--r--drivers/net/ppp_async.c4
-rw-r--r--drivers/net/ppp_generic.c6
-rw-r--r--drivers/net/ppp_synctty.c3
-rw-r--r--drivers/net/pppoe.c156
-rw-r--r--drivers/net/pppox.c2
-rwxr-xr-xdrivers/net/qla3xxx.c5
-rw-r--r--drivers/net/r8169.c3
-rw-r--r--drivers/net/rionet.c1
-rw-r--r--drivers/net/rrunner.c3
-rw-r--r--drivers/net/s2io.c4
-rw-r--r--drivers/net/saa9730.c1
-rw-r--r--drivers/net/sb1000.c2
-rw-r--r--drivers/net/sb1250-mac.c3
-rw-r--r--drivers/net/sc92031.c1
-rw-r--r--drivers/net/seeq8005.c1
-rw-r--r--drivers/net/sgiseeq.c3
-rw-r--r--drivers/net/sis190.c1
-rw-r--r--drivers/net/sis900.c56
-rw-r--r--drivers/net/sk98lin/skge.c12
-rw-r--r--drivers/net/skfp/skfddi.c3
-rw-r--r--drivers/net/skge.c6
-rw-r--r--drivers/net/sky2.c187
-rw-r--r--drivers/net/sky2.h11
-rw-r--r--drivers/net/slip.c2
-rw-r--r--drivers/net/smc911x.c2
-rw-r--r--drivers/net/smc9194.c1
-rw-r--r--drivers/net/smc91x.c1
-rw-r--r--drivers/net/sonic.c2
-rw-r--r--drivers/net/spider_net.c5
-rw-r--r--drivers/net/starfire.c1
-rw-r--r--drivers/net/sun3_82586.c3
-rw-r--r--drivers/net/sun3lance.c5
-rw-r--r--drivers/net/sunbmac.c1
-rw-r--r--drivers/net/sundance.c1
-rw-r--r--drivers/net/sungem.c46
-rw-r--r--drivers/net/sungem.h2
-rw-r--r--drivers/net/sunhme.c35
-rw-r--r--drivers/net/sunlance.c8
-rw-r--r--drivers/net/sunqe.c7
-rw-r--r--drivers/net/tc35815.c1
-rw-r--r--drivers/net/tg3.c82
-rw-r--r--drivers/net/tlan.c4
-rw-r--r--drivers/net/tokenring/3c359.c11
-rw-r--r--drivers/net/tokenring/ibmtr.c1
-rw-r--r--drivers/net/tokenring/lanstreamer.c7
-rw-r--r--drivers/net/tokenring/olympic.c18
-rw-r--r--drivers/net/tokenring/smctr.c6
-rw-r--r--drivers/net/tokenring/tms380tr.c6
-rw-r--r--drivers/net/tsi108_eth.c1
-rw-r--r--drivers/net/tulip/de2104x.c7
-rw-r--r--drivers/net/tulip/de4x5.c12
-rw-r--r--drivers/net/tulip/dmfe.c12
-rw-r--r--drivers/net/tulip/interrupt.c2
-rw-r--r--drivers/net/tulip/tulip_core.c30
-rw-r--r--drivers/net/tulip/uli526x.c22
-rw-r--r--drivers/net/tulip/winbond-840.c5
-rw-r--r--drivers/net/tulip/xircom_cb.c7
-rw-r--r--drivers/net/tulip/xircom_tulip_cb.c7
-rw-r--r--drivers/net/tun.c46
-rw-r--r--drivers/net/typhoon.c1
-rw-r--r--drivers/net/via-rhine.c1
-rw-r--r--drivers/net/via-velocity.c12
-rw-r--r--drivers/net/wan/cosa.c2
-rw-r--r--drivers/net/wan/cycx_x25.c2
-rw-r--r--drivers/net/wan/dlci.c2
-rw-r--r--drivers/net/wan/dscc4.c3
-rw-r--r--drivers/net/wan/farsync.c2
-rw-r--r--drivers/net/wan/hdlc_cisco.c2
-rw-r--r--drivers/net/wan/hdlc_fr.c5
-rw-r--r--drivers/net/wan/hostess_sv11.c2
-rw-r--r--drivers/net/wan/lmc/lmc_main.c16
-rw-r--r--drivers/net/wan/pc300_drv.c6
-rw-r--r--drivers/net/wan/pc300_tty.c6
-rw-r--r--drivers/net/wan/sbni.c5
-rw-r--r--drivers/net/wan/sealevel.c2
-rw-r--r--drivers/net/wan/syncppp.c2
-rw-r--r--drivers/net/wan/z85230.c4
-rw-r--r--drivers/net/wireless/Kconfig120
-rw-r--r--drivers/net/wireless/airo.c11
-rw-r--r--drivers/net/wireless/arlan-main.c1
-rw-r--r--drivers/net/wireless/atmel.c6
-rw-r--r--drivers/net/wireless/bcm43xx/Kconfig3
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_dma.c3
-rw-r--r--drivers/net/wireless/hostap/Kconfig3
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_rx.c23
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_tx.c25
-rw-r--r--drivers/net/wireless/hostap/hostap_ap.c7
-rw-r--r--drivers/net/wireless/hostap/hostap_hw.c7
-rw-r--r--drivers/net/wireless/hostap/hostap_main.c17
-rw-r--r--drivers/net/wireless/ipw2100.c5
-rw-r--r--drivers/net/wireless/ipw2200.c4
-rw-r--r--drivers/net/wireless/netwave_cs.c1
-rw-r--r--drivers/net/wireless/orinoco.c5
-rw-r--r--drivers/net/wireless/prism54/islpci_eth.c23
-rw-r--r--drivers/net/wireless/ray_cs.c4
-rw-r--r--drivers/net/wireless/strip.c2
-rw-r--r--drivers/net/wireless/wavelan.c9
-rw-r--r--drivers/net/wireless/wavelan_cs.c6
-rw-r--r--drivers/net/wireless/zd1201.c6
-rw-r--r--drivers/net/wireless/zd1211rw/Kconfig3
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.c1
-rw-r--r--drivers/net/yellowfin.c1
-rw-r--r--drivers/net/znet.c1
-rw-r--r--drivers/parisc/led.c4
-rw-r--r--drivers/parport/parport_sunbpp.c10
-rw-r--r--drivers/pci/pci-driver.c6
-rw-r--r--drivers/pci/pci.c58
-rw-r--r--drivers/pci/probe.c45
-rw-r--r--drivers/pnp/card.c6
-rw-r--r--drivers/s390/block/dasd.c3
-rw-r--r--drivers/s390/block/dasd_devmap.c58
-rw-r--r--drivers/s390/char/Makefile5
-rw-r--r--drivers/s390/char/con3215.c7
-rw-r--r--drivers/s390/char/con3270.c7
-rw-r--r--drivers/s390/char/sclp.c10
-rw-r--r--drivers/s390/char/sclp.h72
-rw-r--r--drivers/s390/char/sclp_chp.c196
-rw-r--r--drivers/s390/char/sclp_config.c75
-rw-r--r--drivers/s390/char/sclp_cpi.c4
-rw-r--r--drivers/s390/char/sclp_quiesce.c2
-rw-r--r--drivers/s390/char/sclp_rw.c16
-rw-r--r--drivers/s390/char/sclp_sdias.c255
-rw-r--r--drivers/s390/char/sclp_tty.c6
-rw-r--r--drivers/s390/char/sclp_vt220.c8
-rw-r--r--drivers/s390/char/vmlogrdr.c9
-rw-r--r--drivers/s390/char/zcore.c651
-rw-r--r--drivers/s390/cio/Makefile2
-rw-r--r--drivers/s390/cio/ccwgroup.c33
-rw-r--r--drivers/s390/cio/chp.c683
-rw-r--r--drivers/s390/cio/chp.h53
-rw-r--r--drivers/s390/cio/chsc.c1024
-rw-r--r--drivers/s390/cio/chsc.h42
-rw-r--r--drivers/s390/cio/cio.c52
-rw-r--r--drivers/s390/cio/cio.h17
-rw-r--r--drivers/s390/cio/cmf.c2
-rw-r--r--drivers/s390/cio/css.c201
-rw-r--r--drivers/s390/cio/css.h16
-rw-r--r--drivers/s390/cio/device.c252
-rw-r--r--drivers/s390/cio/device_fsm.c8
-rw-r--r--drivers/s390/cio/device_ops.c7
-rw-r--r--drivers/s390/cio/idset.c112
-rw-r--r--drivers/s390/cio/idset.h25
-rw-r--r--drivers/s390/cio/ioasm.h5
-rw-r--r--drivers/s390/crypto/ap_bus.c28
-rw-r--r--drivers/s390/net/claw.c2
-rw-r--r--drivers/s390/net/ctcmain.c51
-rw-r--r--drivers/s390/net/lcs.c3
-rw-r--r--drivers/s390/net/netiucv.c21
-rw-r--r--drivers/s390/net/qeth_eddp.c30
-rw-r--r--drivers/s390/net/qeth_main.c45
-rw-r--r--drivers/s390/net/qeth_proc.c2
-rw-r--r--drivers/s390/net/qeth_tso.h14
-rw-r--r--drivers/s390/s390mach.c25
-rw-r--r--drivers/s390/sysinfo.c18
-rw-r--r--drivers/sbus/char/envctrl.c8
-rw-r--r--drivers/sbus/char/flash.c2
-rw-r--r--drivers/sbus/char/openprom.c25
-rw-r--r--drivers/sbus/char/vfc_dev.c3
-rw-r--r--drivers/sbus/sbus.c4
-rw-r--r--drivers/scsi/3w-xxxx.c11
-rw-r--r--drivers/scsi/Kconfig6
-rw-r--r--drivers/scsi/Makefile3
-rw-r--r--drivers/scsi/esp.c4394
-rw-r--r--drivers/scsi/esp.h406
-rw-r--r--drivers/scsi/esp_scsi.c2711
-rw-r--r--drivers/scsi/esp_scsi.h560
-rw-r--r--drivers/scsi/hosts.c4
-rw-r--r--drivers/scsi/qlogicpti.c4
-rw-r--r--drivers/scsi/scsi_netlink.c5
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c4
-rw-r--r--drivers/scsi/sun_esp.c634
-rw-r--r--drivers/serial/8250.c8
-rw-r--r--drivers/serial/icom.c9
-rw-r--r--drivers/serial/icom.h1
-rw-r--r--drivers/serial/sunsu.c4
-rw-r--r--drivers/spi/spi_s3c24xx.c4
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/atm/cxacru.c411
-rw-r--r--drivers/usb/atm/usbatm.c27
-rw-r--r--drivers/usb/class/cdc-acm.c81
-rw-r--r--drivers/usb/class/cdc-acm.h3
-rw-r--r--drivers/usb/core/Kconfig25
-rw-r--r--drivers/usb/core/devices.c2
-rw-r--r--drivers/usb/core/devio.c109
-rw-r--r--drivers/usb/core/driver.c267
-rw-r--r--drivers/usb/core/hcd.c34
-rw-r--r--drivers/usb/core/hcd.h3
-rw-r--r--drivers/usb/core/hub.c29
-rw-r--r--drivers/usb/core/inode.c2
-rw-r--r--drivers/usb/core/message.c83
-rw-r--r--drivers/usb/core/quirks.c2
-rw-r--r--drivers/usb/core/sysfs.c102
-rw-r--r--drivers/usb/core/usb.c46
-rw-r--r--drivers/usb/core/usb.h26
-rw-r--r--drivers/usb/gadget/Kconfig22
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/ether.c7
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.c2500
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.h579
-rw-r--r--drivers/usb/gadget/gadget_chips.h8
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.c98
-rw-r--r--drivers/usb/gadget/rndis.h2
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-fsl.h4
-rw-r--r--drivers/usb/host/ehci-hub.c4
-rw-r--r--drivers/usb/host/hc_crisv10.c4550
-rw-r--r--drivers/usb/host/hc_crisv10.h289
-rw-r--r--drivers/usb/host/ohci-hcd.c6
-rw-r--r--drivers/usb/host/ohci-pci.c32
-rw-r--r--drivers/usb/host/uhci-q.c16
-rw-r--r--drivers/usb/input/ati_remote2.c89
-rw-r--r--drivers/usb/input/gtco.c5
-rw-r--r--drivers/usb/misc/adutux.c48
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c4
-rw-r--r--drivers/usb/misc/ftdi-elan.c19
-rw-r--r--drivers/usb/misc/iowarrior.c20
-rw-r--r--drivers/usb/misc/ldusb.c3
-rw-r--r--drivers/usb/misc/usblcd.c7
-rw-r--r--drivers/usb/mon/mon_bin.c14
-rw-r--r--drivers/usb/mon/mon_main.c158
-rw-r--r--drivers/usb/mon/mon_text.c315
-rw-r--r--drivers/usb/mon/usb_mon.h6
-rw-r--r--drivers/usb/net/asix.c8
-rw-r--r--drivers/usb/net/catc.c30
-rw-r--r--drivers/usb/net/dm9601.c5
-rw-r--r--drivers/usb/net/gl620a.c2
-rw-r--r--drivers/usb/net/kaweth.c2
-rw-r--r--drivers/usb/net/net1080.c2
-rw-r--r--drivers/usb/net/pegasus.c14
-rw-r--r--drivers/usb/net/rndis_host.c114
-rw-r--r--drivers/usb/net/rtl8150.c1
-rw-r--r--drivers/usb/net/usbnet.c9
-rw-r--r--drivers/usb/net/usbnet.h1
-rw-r--r--drivers/usb/serial/Kconfig6
-rw-r--r--drivers/usb/serial/aircable.c7
-rw-r--r--drivers/usb/serial/ark3116.c3
-rw-r--r--drivers/usb/serial/cp2101.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c23
-rw-r--r--drivers/usb/serial/ftdi_sio.h1
-rw-r--r--drivers/usb/serial/io_edgeport.c139
-rw-r--r--drivers/usb/serial/io_edgeport.h6
-rw-r--r--drivers/usb/serial/ipaq.c1
-rw-r--r--drivers/usb/serial/kl5kusb105.c28
-rw-r--r--drivers/usb/serial/mct_u232.c12
-rw-r--r--drivers/usb/serial/mos7720.c34
-rw-r--r--drivers/usb/serial/mos7840.c233
-rw-r--r--drivers/usb/serial/omninet.c40
-rw-r--r--drivers/usb/serial/option.c23
-rw-r--r--drivers/usb/serial/sierra.c25
-rw-r--r--drivers/usb/serial/visor.c22
-rw-r--r--drivers/usb/serial/whiteheat.c8
-rw-r--r--drivers/usb/serial/whiteheat.h4
-rw-r--r--drivers/usb/storage/libusual.c3
-rw-r--r--drivers/usb/storage/unusual_devs.h9
-rw-r--r--drivers/usb/usb-skeleton.c41
-rw-r--r--drivers/video/Kconfig2
-rw-r--r--drivers/video/aty/atyfb_base.c6
-rw-r--r--drivers/video/aty/radeon_base.c12
-rw-r--r--drivers/video/aty/radeon_monitor.c16
-rw-r--r--drivers/video/aty/radeonfb.h4
-rw-r--r--drivers/video/cg3.c2
-rw-r--r--drivers/video/igafb.c24
772 files changed, 51059 insertions, 19177 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 3a718f51350e..920c975bb6d4 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -72,7 +72,6 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_NEW_LEDS) += leds/
obj-$(CONFIG_INFINIBAND) += infiniband/
-obj-$(CONFIG_IPATH_CORE) += infiniband/
obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
obj-$(CONFIG_CRYPTO) += crypto/
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 0ae8b9310cbf..589b98b7b216 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -758,7 +758,8 @@ static void acpi_thermal_check(void *data)
del_timer(&(tz->timer));
} else {
if (timer_pending(&(tz->timer)))
- mod_timer(&(tz->timer), (HZ * sleep_time) / 1000);
+ mod_timer(&(tz->timer),
+ jiffies + (HZ * sleep_time) / 1000);
else {
tz->timer.data = (unsigned long)tz;
tz->timer.function = acpi_thermal_run;
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index fd5475071acc..268e301775fc 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -47,14 +47,13 @@ static int amba_match(struct device *dev, struct device_driver *drv)
static int amba_uevent(struct device *dev, char **envp, int nr_env, char *buf, int bufsz)
{
struct amba_device *pcdev = to_amba_device(dev);
+ int retval = 0, i = 0, len = 0;
- if (nr_env < 2)
- return -ENOMEM;
-
- snprintf(buf, bufsz, "AMBA_ID=%08x", pcdev->periphid);
- *envp++ = buf;
- *envp++ = NULL;
- return 0;
+ retval = add_uevent_var(envp, nr_env, &i,
+ buf, bufsz, &len,
+ "AMBA_ID=%08x", pcdev->periphid);
+ envp[i] = NULL;
+ return retval;
}
#else
#define amba_uevent NULL
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index f48207865930..8dc3bc4f5863 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -878,6 +878,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
struct ata_port_info *port;
struct pci_dev *host = NULL;
struct sis_chipset *chipset = NULL;
+ struct sis_chipset *sets;
static struct sis_chipset sis_chipsets[] = {
@@ -932,10 +933,11 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
/* We have to find the bridge first */
- for (chipset = &sis_chipsets[0]; chipset->device; chipset++) {
- host = pci_get_device(PCI_VENDOR_ID_SI, chipset->device, NULL);
+ for (sets = &sis_chipsets[0]; sets->device; sets++) {
+ host = pci_get_device(PCI_VENDOR_ID_SI, sets->device, NULL);
if (host != NULL) {
- if (chipset->device == 0x630) { /* SIS630 */
+ chipset = sets; /* Match found */
+ if (sets->device == 0x630) { /* SIS630 */
u8 host_rev;
pci_read_config_byte(host, PCI_REVISION_ID, &host_rev);
if (host_rev >= 0x30) /* 630 ET */
@@ -946,7 +948,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
}
/* Look for concealed bridges */
- if (host == NULL) {
+ if (chipset == NULL) {
/* Second check */
u32 idemisc;
u16 trueid;
diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
index 3c372e08f77d..59651abfa4f8 100644
--- a/drivers/atm/ambassador.c
+++ b/drivers/atm/ambassador.c
@@ -821,7 +821,7 @@ static inline void fill_rx_pool (amb_dev * dev, unsigned char pool,
}
// cast needed as there is no %? for pointer differences
PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li",
- skb, skb->head, (long) (skb->end - skb->head));
+ skb, skb->head, (long) (skb_end_pointer(skb) - skb->head));
rx.handle = virt_to_bus (skb);
rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
if (rx_give (dev, &rx, pool))
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c
index fc518d85543d..02ad83d6b562 100644
--- a/drivers/atm/atmtcp.c
+++ b/drivers/atm/atmtcp.c
@@ -221,7 +221,7 @@ static int atmtcp_v_send(struct atm_vcc *vcc,struct sk_buff *skb)
hdr->vpi = htons(vcc->vpi);
hdr->vci = htons(vcc->vci);
hdr->length = htonl(skb->len);
- memcpy(skb_put(new_skb,skb->len),skb->data,skb->len);
+ skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len);
if (vcc->pop) vcc->pop(vcc,skb);
else dev_kfree_skb(skb);
out_vcc->push(out_vcc,new_skb);
@@ -310,7 +310,7 @@ static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb)
goto done;
}
__net_timestamp(new_skb);
- memcpy(skb_put(new_skb,skb->len),skb->data,skb->len);
+ skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len);
out_vcc->push(out_vcc,new_skb);
atomic_inc(&vcc->stats->tx);
atomic_inc(&out_vcc->stats->rx);
@@ -352,7 +352,7 @@ static struct atm_dev atmtcp_control_dev = {
.ops = &atmtcp_c_dev_ops,
.type = "atmtcp",
.number = 999,
- .lock = SPIN_LOCK_UNLOCKED
+ .lock = __SPIN_LOCK_UNLOCKED(atmtcp_control_dev.lock)
};
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
index 8fccf018f165..0d3a38b1cb0b 100644
--- a/drivers/atm/eni.c
+++ b/drivers/atm/eni.c
@@ -536,7 +536,7 @@ static int rx_aal0(struct atm_vcc *vcc)
return 0;
}
skb_put(skb,length);
- skb_set_timestamp(skb, &eni_vcc->timestamp);
+ skb->tstamp = eni_vcc->timestamp;
DPRINTK("got len %ld\n",length);
if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1;
eni_vcc->rxing++;
@@ -701,7 +701,7 @@ static void get_service(struct atm_dev *dev)
DPRINTK("Grr, servicing VCC %ld twice\n",vci);
continue;
}
- do_gettimeofday(&ENI_VCC(vcc)->timestamp);
+ ENI_VCC(vcc)->timestamp = ktime_get_real();
ENI_VCC(vcc)->next = NULL;
if (vcc->qos.rxtp.traffic_class == ATM_CBR) {
if (eni_dev->fast)
diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h
index 385090c2a580..d04fefb0841f 100644
--- a/drivers/atm/eni.h
+++ b/drivers/atm/eni.h
@@ -59,7 +59,7 @@ struct eni_vcc {
int rxing; /* number of pending PDUs */
int servicing; /* number of waiting VCs (0 or 1) */
int txing; /* number of pending TX bytes */
- struct timeval timestamp; /* for RX timing */
+ ktime_t timestamp; /* for RX timing */
struct atm_vcc *next; /* next pending RX */
struct sk_buff *last; /* last PDU being DMAed (used to carry
discard information) */
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index a7c0ed3107e3..405ee5e09221 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -1,6 +1,4 @@
/*
- $Id: fore200e.c,v 1.5 2000/04/14 10:10:34 davem Exp $
-
A FORE Systems 200E-series driver for ATM on Linux.
Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2003.
@@ -1502,9 +1500,9 @@ fore200e_open(struct atm_vcc *vcc)
/* pseudo-CBR bandwidth requested? */
if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
- down(&fore200e->rate_sf);
+ mutex_lock(&fore200e->rate_mtx);
if (fore200e->available_cell_rate < vcc->qos.txtp.max_pcr) {
- up(&fore200e->rate_sf);
+ mutex_unlock(&fore200e->rate_mtx);
kfree(fore200e_vcc);
vc_map->vcc = NULL;
@@ -1513,7 +1511,7 @@ fore200e_open(struct atm_vcc *vcc)
/* reserve bandwidth */
fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr;
- up(&fore200e->rate_sf);
+ mutex_unlock(&fore200e->rate_mtx);
}
vcc->itf = vcc->dev->number;
@@ -1599,9 +1597,9 @@ fore200e_close(struct atm_vcc* vcc)
/* release reserved bandwidth, if any */
if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
- down(&fore200e->rate_sf);
+ mutex_lock(&fore200e->rate_mtx);
fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
- up(&fore200e->rate_sf);
+ mutex_unlock(&fore200e->rate_mtx);
clear_bit(ATM_VF_HASQOS, &vcc->flags);
}
@@ -2064,16 +2062,16 @@ fore200e_change_qos(struct atm_vcc* vcc,struct atm_qos* qos, int flags)
if ((qos->txtp.traffic_class == ATM_CBR) && (qos->txtp.max_pcr > 0)) {
- down(&fore200e->rate_sf);
+ mutex_lock(&fore200e->rate_mtx);
if (fore200e->available_cell_rate + vcc->qos.txtp.max_pcr < qos->txtp.max_pcr) {
- up(&fore200e->rate_sf);
+ mutex_unlock(&fore200e->rate_mtx);
return -EAGAIN;
}
fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
fore200e->available_cell_rate -= qos->txtp.max_pcr;
- up(&fore200e->rate_sf);
+ mutex_unlock(&fore200e->rate_mtx);
memcpy(&vcc->qos, qos, sizeof(struct atm_qos));
@@ -2459,7 +2457,7 @@ fore200e_initialize(struct fore200e* fore200e)
DPRINTK(2, "device %s being initialized\n", fore200e->name);
- init_MUTEX(&fore200e->rate_sf);
+ mutex_init(&fore200e->rate_mtx);
spin_lock_init(&fore200e->q_lock);
cpq = fore200e->cp_queues = fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET;
diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h
index f9abfdac33e4..b85a54613dea 100644
--- a/drivers/atm/fore200e.h
+++ b/drivers/atm/fore200e.h
@@ -869,7 +869,7 @@ typedef struct fore200e {
struct stats* stats; /* last snapshot of the stats */
- struct semaphore rate_sf; /* protects rate reservation ops */
+ struct mutex rate_mtx; /* protects rate reservation ops */
spinlock_t q_lock; /* protects queue ops */
#ifdef FORE200E_USE_TASKLET
struct tasklet_struct tx_tasklet; /* performs tx interrupt work */
diff --git a/drivers/atm/he.c b/drivers/atm/he.c
index 8510026b690a..d33aba6864c2 100644
--- a/drivers/atm/he.c
+++ b/drivers/atm/he.c
@@ -1901,13 +1901,13 @@ he_service_rbrq(struct he_dev *he_dev, int group)
case ATM_AAL0:
/* 2.10.1.5 raw cell receive */
skb->len = ATM_AAL0_SDU;
- skb->tail = skb->data + skb->len;
+ skb_set_tail_pointer(skb, skb->len);
break;
case ATM_AAL5:
/* 2.10.1.2 aal5 receive */
skb->len = AAL5_LEN(skb->data, he_vcc->pdu_len);
- skb->tail = skb->data + skb->len;
+ skb_set_tail_pointer(skb, skb->len);
#ifdef USE_CHECKSUM_HW
if (vcc->vpi == 0 && vcc->vci >= ATM_NOT_RSV_VCI) {
skb->ip_summed = CHECKSUM_COMPLETE;
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c
index b4b80140c398..057efbc55d38 100644
--- a/drivers/atm/idt77252.c
+++ b/drivers/atm/idt77252.c
@@ -1065,7 +1065,8 @@ dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe)
vcc = vc->rx_vcc;
pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(skb),
- skb->end - skb->data, PCI_DMA_FROMDEVICE);
+ skb_end_pointer(skb) - skb->data,
+ PCI_DMA_FROMDEVICE);
if ((vcc->qos.aal == ATM_AAL0) ||
(vcc->qos.aal == ATM_AAL34)) {
@@ -1194,7 +1195,8 @@ dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe)
}
pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb),
- skb->end - skb->data, PCI_DMA_FROMDEVICE);
+ skb_end_pointer(skb) - skb->data,
+ PCI_DMA_FROMDEVICE);
sb_pool_remove(card, skb);
skb_trim(skb, len);
@@ -1267,7 +1269,7 @@ idt77252_rx_raw(struct idt77252_dev *card)
tail = readl(SAR_REG_RAWCT);
pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(queue),
- queue->end - queue->head - 16,
+ skb_end_pointer(queue) - queue->head - 16,
PCI_DMA_FROMDEVICE);
while (head != tail) {
@@ -1363,7 +1365,8 @@ drop:
queue = card->raw_cell_head;
pci_dma_sync_single_for_cpu(card->pcidev,
IDT77252_PRV_PADDR(queue),
- queue->end - queue->data,
+ (skb_end_pointer(queue) -
+ queue->data),
PCI_DMA_FROMDEVICE);
} else {
card->raw_cell_head = NULL;
@@ -1816,7 +1819,8 @@ push_rx_skb(struct idt77252_dev *card, struct sk_buff *skb, int queue)
u32 handle;
u32 addr;
- skb->data = skb->tail = skb->head;
+ skb->data = skb->head;
+ skb_reset_tail_pointer(skb);
skb->len = 0;
skb_reserve(skb, 16);
@@ -1835,7 +1839,6 @@ push_rx_skb(struct idt77252_dev *card, struct sk_buff *skb, int queue)
skb_put(skb, SAR_FB_SIZE_3);
break;
default:
- dev_kfree_skb(skb);
return -1;
}
@@ -1874,7 +1877,7 @@ add_rx_skb(struct idt77252_dev *card, int queue,
}
paddr = pci_map_single(card->pcidev, skb->data,
- skb->end - skb->data,
+ skb_end_pointer(skb) - skb->data,
PCI_DMA_FROMDEVICE);
IDT77252_PRV_PADDR(skb) = paddr;
@@ -1888,7 +1891,7 @@ add_rx_skb(struct idt77252_dev *card, int queue,
outunmap:
pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb),
- skb->end - skb->data, PCI_DMA_FROMDEVICE);
+ skb_end_pointer(skb) - skb->data, PCI_DMA_FROMDEVICE);
handle = IDT77252_PRV_POOL(skb);
card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL;
@@ -1905,12 +1908,14 @@ recycle_rx_skb(struct idt77252_dev *card, struct sk_buff *skb)
int err;
pci_dma_sync_single_for_device(card->pcidev, IDT77252_PRV_PADDR(skb),
- skb->end - skb->data, PCI_DMA_FROMDEVICE);
+ skb_end_pointer(skb) - skb->data,
+ PCI_DMA_FROMDEVICE);
err = push_rx_skb(card, skb, POOL_QUEUE(handle));
if (err) {
pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb),
- skb->end - skb->data, PCI_DMA_FROMDEVICE);
+ skb_end_pointer(skb) - skb->data,
+ PCI_DMA_FROMDEVICE);
sb_pool_remove(card, skb);
dev_kfree_skb(skb);
}
@@ -3122,7 +3127,8 @@ deinit_card(struct idt77252_dev *card)
if (skb) {
pci_unmap_single(card->pcidev,
IDT77252_PRV_PADDR(skb),
- skb->end - skb->data,
+ (skb_end_pointer(skb) -
+ skb->data),
PCI_DMA_FROMDEVICE);
card->sbpool[i].skb[j] = NULL;
dev_kfree_skb(skb);
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
index aab9b3733d52..14ced85b3f54 100644
--- a/drivers/atm/nicstar.c
+++ b/drivers/atm/nicstar.c
@@ -2208,7 +2208,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe)
if (i == 1 && ns_rsqe_eopdu(rsqe))
*((u32 *) sb->data) |= 0x00000002;
skb_put(sb, NS_AAL0_HEADER);
- memcpy(sb->tail, cell, ATM_CELL_PAYLOAD);
+ memcpy(skb_tail_pointer(sb), cell, ATM_CELL_PAYLOAD);
skb_put(sb, ATM_CELL_PAYLOAD);
ATM_SKB(sb)->vcc = vcc;
__net_timestamp(sb);
@@ -2252,7 +2252,8 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe)
vc->rx_iov = iovb;
NS_SKB(iovb)->iovcnt = 0;
iovb->len = 0;
- iovb->tail = iovb->data = iovb->head;
+ iovb->data = iovb->head;
+ skb_reset_tail_pointer(iovb);
NS_SKB(iovb)->vcc = vcc;
/* IMPORTANT: a pointer to the sk_buff containing the small or large
buffer is stored as iovec base, NOT a pointer to the
@@ -2265,7 +2266,8 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe)
recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, NS_MAX_IOVECS);
NS_SKB(iovb)->iovcnt = 0;
iovb->len = 0;
- iovb->tail = iovb->data = iovb->head;
+ iovb->data = iovb->head;
+ skb_reset_tail_pointer(iovb);
NS_SKB(iovb)->vcc = vcc;
}
iov = &((struct iovec *) iovb->data)[NS_SKB(iovb)->iovcnt++];
@@ -2393,7 +2395,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe)
skb->destructor = ns_lb_destructor;
#endif /* NS_USE_DESTRUCTORS */
skb_push(skb, NS_SMBUFSIZE);
- memcpy(skb->data, sb->data, NS_SMBUFSIZE);
+ skb_copy_from_linear_data(sb, skb->data, NS_SMBUFSIZE);
skb_put(skb, len - NS_SMBUFSIZE);
ATM_SKB(skb)->vcc = vcc;
__net_timestamp(skb);
@@ -2477,7 +2479,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe)
{
/* Copy the small buffer to the huge buffer */
sb = (struct sk_buff *) iov->iov_base;
- memcpy(hb->data, sb->data, iov->iov_len);
+ skb_copy_from_linear_data(sb, hb->data, iov->iov_len);
skb_put(hb, iov->iov_len);
remaining = len - iov->iov_len;
iov++;
@@ -2489,7 +2491,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe)
{
lb = (struct sk_buff *) iov->iov_base;
tocopy = min_t(int, remaining, iov->iov_len);
- memcpy(hb->tail, lb->data, tocopy);
+ skb_copy_from_linear_data(lb, skb_tail_pointer(hb), tocopy);
skb_put(hb, tocopy);
iov++;
remaining -= tocopy;
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
index 22220733f76f..1ec0654665cf 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container);
static struct list_head attribute_container_list;
-static DECLARE_MUTEX(attribute_container_mutex);
+static DEFINE_MUTEX(attribute_container_mutex);
/**
* attribute_container_register - register an attribute container
@@ -77,9 +77,9 @@ attribute_container_register(struct attribute_container *cont)
klist_init(&cont->containers,internal_container_klist_get,
internal_container_klist_put);
- down(&attribute_container_mutex);
+ mutex_lock(&attribute_container_mutex);
list_add_tail(&cont->node, &attribute_container_list);
- up(&attribute_container_mutex);
+ mutex_unlock(&attribute_container_mutex);
return 0;
}
@@ -94,7 +94,7 @@ int
attribute_container_unregister(struct attribute_container *cont)
{
int retval = -EBUSY;
- down(&attribute_container_mutex);
+ mutex_lock(&attribute_container_mutex);
spin_lock(&cont->containers.k_lock);
if (!list_empty(&cont->containers.k_list))
goto out;
@@ -102,7 +102,7 @@ attribute_container_unregister(struct attribute_container *cont)
list_del(&cont->node);
out:
spin_unlock(&cont->containers.k_lock);
- up(&attribute_container_mutex);
+ mutex_unlock(&attribute_container_mutex);
return retval;
}
@@ -145,7 +145,7 @@ attribute_container_add_device(struct device *dev,
{
struct attribute_container *cont;
- down(&attribute_container_mutex);
+ mutex_lock(&attribute_container_mutex);
list_for_each_entry(cont, &attribute_container_list, node) {
struct internal_container *ic;
@@ -173,7 +173,7 @@ attribute_container_add_device(struct device *dev,
attribute_container_add_class_device(&ic->classdev);
klist_add_tail(&ic->node, &cont->containers);
}
- up(&attribute_container_mutex);
+ mutex_unlock(&attribute_container_mutex);
}
/* FIXME: can't break out of this unless klist_iter_exit is also
@@ -211,7 +211,7 @@ attribute_container_remove_device(struct device *dev,
{
struct attribute_container *cont;
- down(&attribute_container_mutex);
+ mutex_lock(&attribute_container_mutex);
list_for_each_entry(cont, &attribute_container_list, node) {
struct internal_container *ic;
struct klist_iter iter;
@@ -234,7 +234,7 @@ attribute_container_remove_device(struct device *dev,
}
}
}
- up(&attribute_container_mutex);
+ mutex_unlock(&attribute_container_mutex);
}
/**
@@ -255,7 +255,7 @@ attribute_container_device_trigger(struct device *dev,
{
struct attribute_container *cont;
- down(&attribute_container_mutex);
+ mutex_lock(&attribute_container_mutex);
list_for_each_entry(cont, &attribute_container_list, node) {
struct internal_container *ic;
struct klist_iter iter;
@@ -273,7 +273,7 @@ attribute_container_device_trigger(struct device *dev,
fn(cont, dev, &ic->classdev);
}
}
- up(&attribute_container_mutex);
+ mutex_unlock(&attribute_container_mutex);
}
/**
@@ -295,12 +295,12 @@ attribute_container_trigger(struct device *dev,
{
struct attribute_container *cont;
- down(&attribute_container_mutex);
+ mutex_lock(&attribute_container_mutex);
list_for_each_entry(cont, &attribute_container_list, node) {
if (cont->match(cont, dev))
fn(cont, dev);
}
- up(&attribute_container_mutex);
+ mutex_unlock(&attribute_container_mutex);
}
/**
diff --git a/drivers/base/base.h b/drivers/base/base.h
index de7e1442ce60..d597f2659b23 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 int bus_attach_device(struct device * dev);
+extern void 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 253868e03c70..1d76e2349654 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -27,6 +27,9 @@
#define to_driver(obj) container_of(obj, struct device_driver, kobj)
+static int __must_check bus_rescan_devices_helper(struct device *dev,
+ void *data);
+
static ssize_t
drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{
@@ -60,8 +63,19 @@ static struct sysfs_ops driver_sysfs_ops = {
static void driver_release(struct kobject * kobj)
{
- struct device_driver * drv = to_driver(kobj);
- complete(&drv->unloaded);
+ /*
+ * Yes this is an empty release function, it is this way because struct
+ * device is always a static object, not a dynamic one. Yes, this is
+ * not nice and bad, but remember, drivers are code, reference counted
+ * by the module count, not a device, which is really data. And yes,
+ * in the future I do want to have all drivers be created dynamically,
+ * and am working toward that goal, but it will take a bit longer...
+ *
+ * But do not let this example give _anyone_ the idea that they can
+ * create a release function without any code in it at all, to do that
+ * is almost always wrong. If you have any questions about this,
+ * please send an email to <greg@kroah.com>
+ */
}
static struct kobj_type ktype_driver = {
@@ -133,7 +147,6 @@ static decl_subsys(bus, &ktype_bus, NULL);
#ifdef CONFIG_HOTPLUG
-
/* Manually detach a device from its associated driver. */
static int driver_helper(struct device *dev, void *data)
{
@@ -199,6 +212,33 @@ static ssize_t driver_bind(struct device_driver *drv,
}
static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
+static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
+{
+ return sprintf(buf, "%d\n", bus->drivers_autoprobe);
+}
+
+static ssize_t store_drivers_autoprobe(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ if (buf[0] == '0')
+ bus->drivers_autoprobe = 0;
+ else
+ bus->drivers_autoprobe = 1;
+ return count;
+}
+
+static ssize_t store_drivers_probe(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ struct device *dev;
+
+ dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
+ if (!dev)
+ return -ENODEV;
+ if (bus_rescan_devices_helper(dev, NULL) != 0)
+ return -EINVAL;
+ return count;
+}
#endif
static struct device * next_device(struct klist_iter * i)
@@ -418,21 +458,21 @@ out_put:
* - Add device to bus's list of devices.
* - Try to attach to driver.
*/
-int bus_attach_device(struct device * dev)
+void bus_attach_device(struct device * dev)
{
struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) {
dev->is_registered = 1;
- ret = device_attach(dev);
- if (ret >= 0) {
+ if (bus->drivers_autoprobe)
+ ret = device_attach(dev);
+ WARN_ON(ret < 0);
+ if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
- ret = 0;
- } else
+ else
dev->is_registered = 0;
}
- return ret;
}
/**
@@ -515,9 +555,41 @@ static void remove_bind_files(struct device_driver *drv)
driver_remove_file(drv, &driver_attr_bind);
driver_remove_file(drv, &driver_attr_unbind);
}
+
+static int add_probe_files(struct bus_type *bus)
+{
+ int retval;
+
+ bus->drivers_probe_attr.attr.name = "drivers_probe";
+ bus->drivers_probe_attr.attr.mode = S_IWUSR;
+ bus->drivers_probe_attr.attr.owner = bus->owner;
+ bus->drivers_probe_attr.store = store_drivers_probe;
+ retval = bus_create_file(bus, &bus->drivers_probe_attr);
+ if (retval)
+ goto out;
+
+ bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe";
+ bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO;
+ bus->drivers_autoprobe_attr.attr.owner = bus->owner;
+ bus->drivers_autoprobe_attr.show = show_drivers_autoprobe;
+ bus->drivers_autoprobe_attr.store = store_drivers_autoprobe;
+ retval = bus_create_file(bus, &bus->drivers_autoprobe_attr);
+ if (retval)
+ bus_remove_file(bus, &bus->drivers_probe_attr);
+out:
+ return retval;
+}
+
+static void remove_probe_files(struct bus_type *bus)
+{
+ bus_remove_file(bus, &bus->drivers_autoprobe_attr);
+ bus_remove_file(bus, &bus->drivers_probe_attr);
+}
#else
static inline int add_bind_files(struct device_driver *drv) { return 0; }
static inline void remove_bind_files(struct device_driver *drv) {}
+static inline int add_probe_files(struct bus_type *bus) { return 0; }
+static inline void remove_probe_files(struct bus_type *bus) {}
#endif
/**
@@ -531,7 +603,7 @@ int bus_add_driver(struct device_driver *drv)
int error = 0;
if (!bus)
- return 0;
+ return -EINVAL;
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name);
@@ -541,9 +613,11 @@ int bus_add_driver(struct device_driver *drv)
if ((error = kobject_register(&drv->kobj)))
goto out_put_bus;
- error = driver_attach(drv);
- if (error)
- goto out_unregister;
+ if (drv->bus->drivers_autoprobe) {
+ error = driver_attach(drv);
+ if (error)
+ goto out_unregister;
+ }
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv);
@@ -605,8 +679,6 @@ static int __must_check bus_rescan_devices_helper(struct device *dev,
ret = device_attach(dev);
if (dev->parent)
up(&dev->parent->sem);
- if (ret > 0)
- ret = 0;
}
return ret < 0 ? ret : 0;
}
@@ -762,6 +834,12 @@ int bus_register(struct bus_type * bus)
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, NULL, NULL);
+
+ bus->drivers_autoprobe = 1;
+ retval = add_probe_files(bus);
+ if (retval)
+ goto bus_probe_files_fail;
+
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
@@ -770,6 +848,8 @@ int bus_register(struct bus_type * bus)
return 0;
bus_attrs_fail:
+ remove_probe_files(bus);
+bus_probe_files_fail:
kset_unregister(&bus->drivers);
bus_drivers_fail:
kset_unregister(&bus->devices);
@@ -779,7 +859,6 @@ out:
return retval;
}
-
/**
* bus_unregister - remove a bus from the system
* @bus: bus.
@@ -791,6 +870,7 @@ void bus_unregister(struct bus_type * bus)
{
pr_debug("bus %s: unregistering\n", bus->name);
bus_remove_attrs(bus);
+ remove_probe_files(bus);
kset_unregister(&bus->drivers);
kset_unregister(&bus->devices);
subsystem_unregister(&bus->subsys);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index d5968128be2b..80bbb2074636 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -145,6 +145,7 @@ int class_register(struct class * cls)
INIT_LIST_HEAD(&cls->children);
INIT_LIST_HEAD(&cls->devices);
INIT_LIST_HEAD(&cls->interfaces);
+ kset_init(&cls->class_dirs);
init_MUTEX(&cls->sem);
error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
if (error)
@@ -163,7 +164,6 @@ int class_register(struct class * cls)
void class_unregister(struct class * cls)
{
pr_debug("device class '%s': unregistering\n", cls->name);
- kobject_unregister(cls->virtual_dir);
remove_class_attrs(cls);
subsystem_unregister(&cls->subsys);
}
diff --git a/drivers/base/core.c b/drivers/base/core.c
index d7fcf823a42a..8aa090da1cd7 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -43,7 +43,8 @@ int (*platform_notify_remove)(struct device * dev) = NULL;
const char *dev_driver_string(struct device *dev)
{
return dev->driver ? dev->driver->name :
- (dev->bus ? dev->bus->name : "");
+ (dev->bus ? dev->bus->name :
+ (dev->class ? dev->class->name : ""));
}
EXPORT_SYMBOL(dev_driver_string);
@@ -119,6 +120,8 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
if (ktype == &ktype_device) {
struct device *dev = to_dev(kobj);
+ if (dev->uevent_suppress)
+ return 0;
if (dev->bus)
return 1;
if (dev->class)
@@ -156,6 +159,11 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
"MINOR=%u", MINOR(dev->devt));
}
+ if (dev->type && dev->type->name)
+ add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "DEVTYPE=%s", dev->type->name);
+
if (dev->driver)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
@@ -238,71 +246,152 @@ static struct kset_uevent_ops device_uevent_ops = {
.uevent = dev_uevent,
};
+static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct kobject *top_kobj;
+ struct kset *kset;
+ char *envp[32];
+ char data[PAGE_SIZE];
+ char *pos;
+ int i;
+ size_t count = 0;
+ int retval;
+
+ /* search the kset, the device belongs to */
+ top_kobj = &dev->kobj;
+ if (!top_kobj->kset && top_kobj->parent) {
+ do {
+ top_kobj = top_kobj->parent;
+ } while (!top_kobj->kset && top_kobj->parent);
+ }
+ if (!top_kobj->kset)
+ goto out;
+ kset = top_kobj->kset;
+ if (!kset->uevent_ops || !kset->uevent_ops->uevent)
+ goto out;
+
+ /* respect filter */
+ if (kset->uevent_ops && kset->uevent_ops->filter)
+ if (!kset->uevent_ops->filter(kset, &dev->kobj))
+ goto out;
+
+ /* let the kset specific function add its keys */
+ pos = data;
+ retval = kset->uevent_ops->uevent(kset, &dev->kobj,
+ envp, ARRAY_SIZE(envp),
+ pos, PAGE_SIZE);
+ if (retval)
+ goto out;
+
+ /* copy keys to file */
+ for (i = 0; envp[i]; i++) {
+ pos = &buf[count];
+ count += sprintf(pos, "%s\n", envp[i]);
+ }
+out:
+ return count;
+}
+
static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
+ if (memcmp(buf, "add", 3) != 0)
+ dev_err(dev, "uevent: unsupported action-string; this will "
+ "be ignored in a future kernel version");
kobject_uevent(&dev->kobj, KOBJ_ADD);
return count;
}
-static int device_add_groups(struct device *dev)
+static int device_add_attributes(struct device *dev,
+ struct device_attribute *attrs)
+{
+ int error = 0;
+ int i;
+
+ if (attrs) {
+ for (i = 0; attr_name(attrs[i]); i++) {
+ error = device_create_file(dev, &attrs[i]);
+ if (error)
+ break;
+ }
+ if (error)
+ while (--i >= 0)
+ device_remove_file(dev, &attrs[i]);
+ }
+ return error;
+}
+
+static void device_remove_attributes(struct device *dev,
+ struct device_attribute *attrs)
{
int i;
+
+ if (attrs)
+ for (i = 0; attr_name(attrs[i]); i++)
+ device_remove_file(dev, &attrs[i]);
+}
+
+static int device_add_groups(struct device *dev,
+ struct attribute_group **groups)
+{
int error = 0;
+ int i;
- if (dev->groups) {
- for (i = 0; dev->groups[i]; i++) {
- error = sysfs_create_group(&dev->kobj, dev->groups[i]);
+ if (groups) {
+ for (i = 0; groups[i]; i++) {
+ error = sysfs_create_group(&dev->kobj, groups[i]);
if (error) {
while (--i >= 0)
- sysfs_remove_group(&dev->kobj, dev->groups[i]);
- goto out;
+ sysfs_remove_group(&dev->kobj, groups[i]);
+ break;
}
}
}
-out:
return error;
}
-static void device_remove_groups(struct device *dev)
+static void device_remove_groups(struct device *dev,
+ struct attribute_group **groups)
{
int i;
- if (dev->groups) {
- for (i = 0; dev->groups[i]; i++) {
- sysfs_remove_group(&dev->kobj, dev->groups[i]);
- }
- }
+
+ if (groups)
+ for (i = 0; groups[i]; i++)
+ sysfs_remove_group(&dev->kobj, groups[i]);
}
static int device_add_attrs(struct device *dev)
{
struct class *class = dev->class;
struct device_type *type = dev->type;
- int error = 0;
- int i;
+ int error;
- if (class && 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 (class) {
+ error = device_add_attributes(dev, class->dev_attrs);
if (error)
- while (--i >= 0)
- device_remove_file(dev, &class->dev_attrs[i]);
+ return error;
}
- if (type && type->attrs) {
- for (i = 0; attr_name(type->attrs[i]); i++) {
- error = device_create_file(dev, &type->attrs[i]);
- if (error)
- break;
- }
+ if (type) {
+ error = device_add_groups(dev, type->groups);
if (error)
- while (--i >= 0)
- device_remove_file(dev, &type->attrs[i]);
+ goto err_remove_class_attrs;
}
+ error = device_add_groups(dev, dev->groups);
+ if (error)
+ goto err_remove_type_groups;
+
+ return 0;
+
+ err_remove_type_groups:
+ if (type)
+ device_remove_groups(dev, type->groups);
+ err_remove_class_attrs:
+ if (class)
+ device_remove_attributes(dev, class->dev_attrs);
+
return error;
}
@@ -310,17 +399,14 @@ static void device_remove_attrs(struct device *dev)
{
struct class *class = dev->class;
struct device_type *type = dev->type;
- int i;
- if (class && class->dev_attrs) {
- for (i = 0; attr_name(class->dev_attrs[i]); i++)
- device_remove_file(dev, &class->dev_attrs[i]);
- }
+ device_remove_groups(dev, dev->groups);
- if (type && type->attrs) {
- for (i = 0; attr_name(type->attrs[i]); i++)
- device_remove_file(dev, &type->attrs[i]);
- }
+ if (type)
+ device_remove_groups(dev, type->groups);
+
+ if (class)
+ device_remove_attributes(dev, class->dev_attrs);
}
@@ -394,9 +480,10 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
EXPORT_SYMBOL_GPL(device_remove_bin_file);
/**
- * device_schedule_callback - helper to schedule a callback for a device
+ * device_schedule_callback_owner - helper to schedule a callback for a device
* @dev: device.
* @func: callback function to invoke later.
+ * @owner: module owning the callback routine
*
* Attribute methods must not unregister themselves or their parent device
* (which would amount to the same thing). Attempts to do so will deadlock,
@@ -407,20 +494,23 @@ EXPORT_SYMBOL_GPL(device_remove_bin_file);
* argument in the workqueue's process context. @dev will be pinned until
* @func returns.
*
+ * This routine is usually called via the inline device_schedule_callback(),
+ * which automatically sets @owner to THIS_MODULE.
+ *
* Returns 0 if the request was submitted, -ENOMEM if storage could not
- * be allocated.
+ * be allocated, -ENODEV if a reference to @owner isn't available.
*
* NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an
* underlying sysfs routine (since it is intended for use by attribute
* methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
*/
-int device_schedule_callback(struct device *dev,
- void (*func)(struct device *))
+int device_schedule_callback_owner(struct device *dev,
+ void (*func)(struct device *), struct module *owner)
{
return sysfs_schedule_callback(&dev->kobj,
- (void (*)(void *)) func, dev);
+ (void (*)(void *)) func, dev, owner);
}
-EXPORT_SYMBOL_GPL(device_schedule_callback);
+EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
static void klist_children_get(struct klist_node *n)
{
@@ -477,34 +567,58 @@ static struct kobject * get_device_parent(struct device *dev,
return NULL;
}
#else
-static struct kobject * virtual_device_parent(struct device *dev)
+static struct kobject *virtual_device_parent(struct device *dev)
{
- if (!dev->class)
- return ERR_PTR(-ENODEV);
+ static struct kobject *virtual_dir = NULL;
- if (!dev->class->virtual_dir) {
- static struct kobject *virtual_dir = NULL;
+ if (!virtual_dir)
+ virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
- 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);
- }
-
- return dev->class->virtual_dir;
+ return virtual_dir;
}
static struct kobject * get_device_parent(struct device *dev,
struct device *parent)
{
- /* if this is a class device, and has no parent, create one */
- if ((dev->class) && (parent == NULL)) {
- return virtual_device_parent(dev);
- } else if (parent)
+ if (dev->class) {
+ struct kobject *kobj = NULL;
+ struct kobject *parent_kobj;
+ struct kobject *k;
+
+ /*
+ * If we have no parent, we live in "virtual".
+ * Class-devices with a bus-device as parent, live
+ * in a class-directory to prevent namespace collisions.
+ */
+ if (parent == NULL)
+ parent_kobj = virtual_device_parent(dev);
+ else if (parent->class)
+ return &parent->kobj;
+ else
+ parent_kobj = &parent->kobj;
+
+ /* find our class-directory at the parent and reference it */
+ spin_lock(&dev->class->class_dirs.list_lock);
+ list_for_each_entry(k, &dev->class->class_dirs.list, entry)
+ if (k->parent == parent_kobj) {
+ kobj = kobject_get(k);
+ break;
+ }
+ spin_unlock(&dev->class->class_dirs.list_lock);
+ if (kobj)
+ return kobj;
+
+ /* or create a new class-directory at the parent device */
+ return kobject_kset_add_dir(&dev->class->class_dirs,
+ parent_kobj, dev->class->name);
+ }
+
+ if (parent)
return &parent->kobj;
return NULL;
}
-
#endif
+
static int setup_parent(struct device *dev, struct device *parent)
{
struct kobject *kobj;
@@ -541,7 +655,6 @@ int device_add(struct device *dev)
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
parent = get_device(dev->parent);
-
error = setup_parent(dev, parent);
if (error)
goto Error;
@@ -562,10 +675,11 @@ int device_add(struct device *dev)
BUS_NOTIFY_ADD_DEVICE, dev);
dev->uevent_attr.attr.name = "uevent";
- dev->uevent_attr.attr.mode = S_IWUSR;
+ dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR;
if (dev->driver)
dev->uevent_attr.attr.owner = dev->driver->owner;
dev->uevent_attr.store = store_uevent;
+ dev->uevent_attr.show = show_uevent;
error = device_create_file(dev, &dev->uevent_attr);
if (error)
goto attrError;
@@ -614,16 +728,12 @@ int device_add(struct device *dev)
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)))
goto BusError;
- if (!dev->uevent_suppress)
- kobject_uevent(&dev->kobj, KOBJ_ADD);
- if ((error = bus_attach_device(dev)))
- goto AttachError;
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+ bus_attach_device(dev);
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
@@ -639,19 +749,15 @@ int device_add(struct device *dev)
up(&dev->class->sem);
}
Done:
- kfree(class_name);
+ kfree(class_name);
put_device(dev);
return error;
- AttachError:
- bus_remove_device(dev);
BusError:
device_pm_remove(dev);
PMError:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
- device_remove_groups(dev);
- GroupError:
device_remove_attrs(dev);
AttrsError:
if (dev->devt_attr) {
@@ -677,15 +783,6 @@ int device_add(struct device *dev)
#endif
sysfs_remove_link(&dev->kobj, "device");
}
-
- 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);
}
ueventattrError:
device_remove_file(dev, &dev->uevent_attr);
@@ -796,9 +893,33 @@ void device_del(struct device * dev)
/* remove the device from the class list */
list_del_init(&dev->node);
up(&dev->class->sem);
+
+ /* If we live in a parent class-directory, unreference it */
+ if (dev->kobj.parent->kset == &dev->class->class_dirs) {
+ struct device *d;
+ int other = 0;
+
+ /*
+ * if we are the last child of our class, delete
+ * our class-directory at this parent
+ */
+ down(&dev->class->sem);
+ list_for_each_entry(d, &dev->class->devices, node) {
+ if (d == dev)
+ continue;
+ if (d->kobj.parent == dev->kobj.parent) {
+ other = 1;
+ break;
+ }
+ }
+ if (!other)
+ kobject_del(dev->kobj.parent);
+
+ kobject_put(dev->kobj.parent);
+ up(&dev->class->sem);
+ }
}
device_remove_file(dev, &dev->uevent_attr);
- device_remove_groups(dev);
device_remove_attrs(dev);
bus_remove_device(dev);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 6a48824e43ff..18dba8e78da7 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -94,19 +94,11 @@ int device_bind_driver(struct device *dev)
return ret;
}
-struct stupid_thread_structure {
- struct device_driver *drv;
- struct device *dev;
-};
-
static atomic_t probe_count = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
-static int really_probe(void *void_data)
+static int really_probe(struct device *dev, struct device_driver *drv)
{
- struct stupid_thread_structure *data = void_data;
- struct device_driver *drv = data->drv;
- struct device *dev = data->dev;
int ret = 0;
atomic_inc(&probe_count);
@@ -154,7 +146,6 @@ probe_failed:
*/
ret = 0;
done:
- kfree(data);
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
@@ -186,16 +177,14 @@ int driver_probe_done(void)
* 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 returns 1 if a match is found, -ENODEV if the device is
+ * not registered, 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))
@@ -206,19 +195,7 @@ int driver_probe_device(struct device_driver * drv, struct device * dev)
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- 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 = really_probe(data);
- } else
- ret = really_probe(data);
+ ret = really_probe(dev, drv);
done:
return ret;
@@ -230,30 +207,57 @@ static int __device_attach(struct device_driver * drv, void * data)
return driver_probe_device(drv, dev);
}
+static int device_probe_drivers(void *data)
+{
+ struct device *dev = data;
+ int ret = 0;
+
+ if (dev->bus) {
+ down(&dev->sem);
+ ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+ up(&dev->sem);
+ }
+ return ret;
+}
+
/**
* device_attach - try to attach device to a driver.
* @dev: device.
*
* Walk the list of drivers that the bus has and call
* driver_probe_device() for each pair. If a compatible
- * pair is found, break out and return.
+ * pair is found, break out and return. If the bus specifies
+ * multithreaded probing, walking the list of drivers is done
+ * on a probing thread.
*
* Returns 1 if the device was bound to a driver;
- * 0 if no matching device was found; error code otherwise.
+ * 0 if no matching device was found or multithreaded probing is done;
+ * -ENODEV if the device is not registered.
*
* When called for a USB interface, @dev->parent->sem must be held.
*/
int device_attach(struct device * dev)
{
int ret = 0;
+ struct task_struct *probe_task = ERR_PTR(-ENOMEM);
down(&dev->sem);
if (dev->driver) {
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
- } else
- ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+ else {
+ dev->driver = NULL;
+ ret = 0;
+ }
+ } else {
+ if (dev->bus->multithread_probe)
+ probe_task = kthread_run(device_probe_drivers, dev,
+ "probe-%s", dev->bus_id);
+ if(IS_ERR(probe_task))
+ ret = bus_for_each_drv(dev->bus, NULL, dev,
+ __device_attach);
+ }
up(&dev->sem);
return ret;
}
diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c
index cd467c9f33b3..9406259754ad 100644
--- a/drivers/base/dmapool.c
+++ b/drivers/base/dmapool.c
@@ -37,7 +37,7 @@ struct dma_page { /* cacheable header for 'allocation' bytes */
#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000)
-static DECLARE_MUTEX (pools_lock);
+static DEFINE_MUTEX (pools_lock);
static ssize_t
show_pools (struct device *dev, struct device_attribute *attr, char *buf)
@@ -55,7 +55,7 @@ show_pools (struct device *dev, struct device_attribute *attr, char *buf)
size -= temp;
next += temp;
- down (&pools_lock);
+ mutex_lock(&pools_lock);
list_for_each_entry(pool, &dev->dma_pools, pools) {
unsigned pages = 0;
unsigned blocks = 0;
@@ -73,7 +73,7 @@ show_pools (struct device *dev, struct device_attribute *attr, char *buf)
size -= temp;
next += temp;
}
- up (&pools_lock);
+ mutex_unlock(&pools_lock);
return PAGE_SIZE - size;
}
@@ -143,7 +143,7 @@ dma_pool_create (const char *name, struct device *dev,
if (dev) {
int ret;
- down (&pools_lock);
+ mutex_lock(&pools_lock);
if (list_empty (&dev->dma_pools))
ret = device_create_file (dev, &dev_attr_pools);
else
@@ -155,7 +155,7 @@ dma_pool_create (const char *name, struct device *dev,
kfree(retval);
retval = NULL;
}
- up (&pools_lock);
+ mutex_unlock(&pools_lock);
} else
INIT_LIST_HEAD (&retval->pools);
@@ -231,11 +231,11 @@ pool_free_page (struct dma_pool *pool, struct dma_page *page)
void
dma_pool_destroy (struct dma_pool *pool)
{
- down (&pools_lock);
+ mutex_lock(&pools_lock);
list_del (&pool->pools);
if (pool->dev && list_empty (&pool->dev->dma_pools))
device_remove_file (pool->dev, &dev_attr_pools);
- up (&pools_lock);
+ mutex_unlock(&pools_lock);
while (!list_empty (&pool->page_list)) {
struct dma_page *page;
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 082bfded3854..eb11475293ed 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -149,10 +149,6 @@ void put_driver(struct device_driver * drv)
* We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
- *
- * The one interesting aspect is that we setup @drv->unloaded
- * as a completion that gets complete when the driver reference
- * count reaches 0.
*/
int driver_register(struct device_driver * drv)
{
@@ -162,35 +158,19 @@ int driver_register(struct device_driver * drv)
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
klist_init(&drv->klist_devices, NULL, NULL);
- init_completion(&drv->unloaded);
return bus_add_driver(drv);
}
-
/**
* driver_unregister - remove driver from system.
* @drv: driver.
*
* Again, we pass off most of the work to the bus-level call.
- *
- * Though, once that is done, we wait until @drv->unloaded is completed.
- * This will block until the driver refcount reaches 0, and it is
- * released. Only modular drivers will call this function, and we
- * have to guarantee that it won't complete, letting the driver
- * unload until all references are gone.
*/
void driver_unregister(struct device_driver * drv)
{
bus_remove_driver(drv);
- /*
- * If the driver is a module, we are probably in
- * the module unload path, and we want to wait
- * for everything to unload before we can actually
- * finish the unload.
- */
- if (drv->owner)
- wait_for_completion(&drv->unloaded);
}
/**
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index c0a979a5074b..97ab5bd1c4d6 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -31,8 +31,6 @@ enum {
FW_STATUS_LOADING,
FW_STATUS_DONE,
FW_STATUS_ABORT,
- FW_STATUS_READY,
- FW_STATUS_READY_NOHOTPLUG,
};
static int loading_timeout = 60; /* In seconds */
@@ -96,9 +94,6 @@ static int firmware_uevent(struct device *dev, char **envp, int num_envp,
struct firmware_priv *fw_priv = dev_get_drvdata(dev);
int i = 0, len = 0;
- if (!test_bit(FW_STATUS_READY, &fw_priv->status))
- return -ENODEV;
-
if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"FIRMWARE=%s", fw_priv->fw_id))
return -ENOMEM;
@@ -333,6 +328,7 @@ static int fw_register_device(struct device **dev_p, const char *fw_name,
f_dev->parent = device;
f_dev->class = &firmware_class;
dev_set_drvdata(f_dev, fw_priv);
+ f_dev->uevent_suppress = 1;
retval = device_register(f_dev);
if (retval) {
printk(KERN_ERR "%s: device_register failed\n",
@@ -382,9 +378,7 @@ static int fw_setup_device(struct firmware *fw, struct device **dev_p,
}
if (uevent)
- set_bit(FW_STATUS_READY, &fw_priv->status);
- else
- set_bit(FW_STATUS_READY_NOHOTPLUG, &fw_priv->status);
+ f_dev->uevent_suppress = 0;
*dev_p = f_dev;
goto out;
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index bbbb973a9d3c..05dc8764e765 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -29,6 +29,9 @@ LIST_HEAD(dpm_off_irq);
DECLARE_MUTEX(dpm_sem);
DECLARE_MUTEX(dpm_list_sem);
+int (*platform_enable_wakeup)(struct device *dev, int is_on);
+
+
/**
* device_pm_set_parent - Specify power dependency.
* @dev: Device who needs power.
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index 020be36705a6..a2c64188d713 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -26,7 +26,9 @@ int resume_device(struct device * dev)
TRACE_DEVICE(dev);
TRACE_RESUME(0);
+
down(&dev->sem);
+
if (dev->power.pm_parent
&& dev->power.pm_parent->power.power_state.event) {
dev_err(dev, "PM: resume from %d, parent %s still %d\n",
@@ -34,15 +36,24 @@ int resume_device(struct device * dev)
dev->power.pm_parent->bus_id,
dev->power.pm_parent->power.power_state.event);
}
+
if (dev->bus && dev->bus->resume) {
dev_dbg(dev,"resuming\n");
error = dev->bus->resume(dev);
}
- if (dev->class && dev->class->resume) {
+
+ if (!error && dev->type && dev->type->resume) {
+ dev_dbg(dev,"resuming\n");
+ error = dev->type->resume(dev);
+ }
+
+ if (!error && dev->class && dev->class->resume) {
dev_dbg(dev,"class resume\n");
error = dev->class->resume(dev);
}
+
up(&dev->sem);
+
TRACE_RESUME(error);
return error;
}
diff --git a/drivers/base/power/shutdown.c b/drivers/base/power/shutdown.c
index 3483ae4d57f5..58b6f77a1b34 100644
--- a/drivers/base/power/shutdown.c
+++ b/drivers/base/power/shutdown.c
@@ -36,7 +36,6 @@ void device_shutdown(void)
{
struct device * dev, *devn;
- down_write(&devices_subsys.rwsem);
list_for_each_entry_safe_reverse(dev, devn, &devices_subsys.kset.list,
kobj.entry) {
if (dev->bus && dev->bus->shutdown) {
@@ -47,7 +46,6 @@ void device_shutdown(void)
dev->driver->shutdown(dev);
}
}
- up_write(&devices_subsys.rwsem);
sysdev_shutdown();
}
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index ece136bf97e3..42d2b86ba765 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -78,6 +78,18 @@ int suspend_device(struct device * dev, pm_message_t state)
suspend_report_result(dev->class->suspend, error);
}
+ if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) {
+ dev_dbg(dev, "%s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
+ error = dev->type->suspend(dev, state);
+ suspend_report_result(dev->type->suspend, error);
+ }
+
if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "%s%s\n",
suspend_verb(state.event),
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h
index 2308e83e5f33..1d8466817943 100644
--- a/drivers/block/aoe/aoe.h
+++ b/drivers/block/aoe/aoe.h
@@ -48,6 +48,15 @@ struct aoe_hdr {
__be32 tag;
};
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct aoe_hdr *aoe_hdr(const struct sk_buff *skb)
+{
+ return (struct aoe_hdr *)skb_mac_header(skb);
+}
+#endif
+
struct aoe_atahdr {
unsigned char aflags;
unsigned char errfeat;
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index 8d17d8df3662..1a6aeac5a1c3 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -27,7 +27,8 @@ new_skb(ulong len)
skb = alloc_skb(len, GFP_ATOMIC);
if (skb) {
- skb->nh.raw = skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
skb->protocol = __constant_htons(ETH_P_AOE);
skb->priority = 0;
skb->next = skb->prev = NULL;
@@ -118,7 +119,7 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f)
/* initialize the headers & frame */
skb = f->skb;
- h = (struct aoe_hdr *) skb->mac.raw;
+ h = aoe_hdr(skb);
ah = (struct aoe_atahdr *) (h+1);
skb_put(skb, sizeof *h + sizeof *ah);
memset(h, 0, skb->len);
@@ -207,7 +208,7 @@ aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff **tail)
skb->dev = ifp;
if (sl_tail == NULL)
sl_tail = skb;
- h = (struct aoe_hdr *) skb->mac.raw;
+ h = aoe_hdr(skb);
memset(h, 0, sizeof *h + sizeof *ch);
memset(h->dst, 0xff, sizeof h->dst);
@@ -300,7 +301,7 @@ rexmit(struct aoedev *d, struct frame *f)
aoechr_error(buf);
skb = f->skb;
- h = (struct aoe_hdr *) skb->mac.raw;
+ h = aoe_hdr(skb);
ah = (struct aoe_atahdr *) (h+1);
f->tag = n;
h->tag = cpu_to_be32(n);
@@ -529,7 +530,7 @@ aoecmd_ata_rsp(struct sk_buff *skb)
char ebuf[128];
u16 aoemajor;
- hin = (struct aoe_hdr *) skb->mac.raw;
+ hin = aoe_hdr(skb);
aoemajor = be16_to_cpu(get_unaligned(&hin->major));
d = aoedev_by_aoeaddr(aoemajor, hin->minor);
if (d == NULL) {
@@ -561,7 +562,7 @@ aoecmd_ata_rsp(struct sk_buff *skb)
calc_rttavg(d, tsince(f->tag));
ahin = (struct aoe_atahdr *) (hin+1);
- hout = (struct aoe_hdr *) f->skb->mac.raw;
+ hout = aoe_hdr(f->skb);
ahout = (struct aoe_atahdr *) (hout+1);
buf = f->buf;
@@ -695,7 +696,7 @@ aoecmd_ata_id(struct aoedev *d)
/* initialize the headers & frame */
skb = f->skb;
- h = (struct aoe_hdr *) skb->mac.raw;
+ h = aoe_hdr(skb);
ah = (struct aoe_atahdr *) (h+1);
skb_put(skb, sizeof *h + sizeof *ah);
memset(h, 0, skb->len);
@@ -726,7 +727,7 @@ aoecmd_cfg_rsp(struct sk_buff *skb)
enum { MAXFRAMES = 16 };
u16 n;
- h = (struct aoe_hdr *) skb->mac.raw;
+ h = aoe_hdr(skb);
ch = (struct aoe_cfghdr *) (h+1);
/*
diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c
index aab6d91a2c22..f9ddfda4d9cb 100644
--- a/drivers/block/aoe/aoenet.c
+++ b/drivers/block/aoe/aoenet.c
@@ -123,7 +123,7 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt,
goto exit;
skb_push(skb, ETH_HLEN); /* (1) */
- h = (struct aoe_hdr *) skb->mac.raw;
+ h = aoe_hdr(skb);
n = be32_to_cpu(get_unaligned(&h->tag));
if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31))
goto exit;
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 14d780666c0a..65a725cd3422 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -3423,6 +3423,25 @@ static void cciss_remove_one(struct pci_dev *pdev)
"already be removed \n");
return;
}
+
+ remove_proc_entry(hba[i]->devname, proc_cciss);
+ unregister_blkdev(hba[i]->major, hba[i]->devname);
+
+ /* remove it from the disk list */
+ for (j = 0; j < CISS_MAX_LUN; j++) {
+ struct gendisk *disk = hba[i]->gendisk[j];
+ if (disk) {
+ request_queue_t *q = disk->queue;
+
+ if (disk->flags & GENHD_FL_UP)
+ del_gendisk(disk);
+ if (q)
+ blk_cleanup_queue(q);
+ }
+ }
+
+ cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
+
/* Turn board interrupts off and send the flush cache command */
/* sendcmd will turn off interrupt, and send the flush...
* To write all data in the battery backed cache to disks */
@@ -3444,22 +3463,6 @@ static void cciss_remove_one(struct pci_dev *pdev)
#endif /* CONFIG_PCI_MSI */
iounmap(hba[i]->vaddr);
- cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
- unregister_blkdev(hba[i]->major, hba[i]->devname);
- remove_proc_entry(hba[i]->devname, proc_cciss);
-
- /* remove it from the disk list */
- for (j = 0; j < CISS_MAX_LUN; j++) {
- struct gendisk *disk = hba[i]->gendisk[j];
- if (disk) {
- request_queue_t *q = disk->queue;
-
- if (disk->flags & GENHD_FL_UP)
- del_gendisk(disk);
- if (q)
- blk_cleanup_queue(q);
- }
- }
pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(CommandList_struct),
hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle);
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index c852eed91e4b..1eeb8f2cde71 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -140,7 +140,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
#include <linux/blkdev.h>
#include <asm/uaccess.h>
-static spinlock_t pcd_lock;
+static DEFINE_SPINLOCK(pcd_lock);
module_param(verbose, bool, 0644);
module_param(major, int, 0);
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
index 7cdaa1951260..5826508f6731 100644
--- a/drivers/block/paride/pf.c
+++ b/drivers/block/paride/pf.c
@@ -154,7 +154,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_LUN, D_DLY};
#include <linux/blkpg.h>
#include <asm/uaccess.h>
-static spinlock_t pf_spin_lock;
+static DEFINE_SPINLOCK(pf_spin_lock);
module_param(verbose, bool, 0644);
module_param(major, int, 0);
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index a4fb70383188..f1b9dd7d47d6 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -777,7 +777,8 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *
rq->cmd_flags |= REQ_QUIET;
blk_execute_rq(rq->q, pd->bdev->bd_disk, rq, 0);
- ret = rq->errors;
+ if (rq->errors)
+ ret = -EIO;
out:
blk_put_request(rq);
return ret;
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index 2098eff91e14..746a118a9b52 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -2132,10 +2132,13 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev,
if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK) {
/* BULK in or out? */
- if (ep->bEndpointAddress & USB_DIR_IN)
- ep_in = ep;
- else
- ep_out = ep;
+ if (ep->bEndpointAddress & USB_DIR_IN) {
+ if (ep_in == NULL)
+ ep_in = ep;
+ } else {
+ if (ep_out == NULL)
+ ep_out = ep;
+ }
}
}
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 4c766f36d884..b990805806af 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -527,7 +527,7 @@ static int bfusb_send_frame(struct sk_buff *skb)
buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
memcpy(skb_put(nskb, 3), buf, 3);
- memcpy(skb_put(nskb, size), skb->data + sent, size);
+ skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size);
sent += size;
count -= size;
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index acfb6a430dcc..851de4d5b7de 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -461,20 +461,20 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
switch (info->rx_state) {
case RECV_WAIT_EVENT_HEADER:
- eh = (struct hci_event_hdr *)(info->rx_skb->data);
+ eh = hci_event_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = eh->plen;
break;
case RECV_WAIT_ACL_HEADER:
- ah = (struct hci_acl_hdr *)(info->rx_skb->data);
+ ah = hci_acl_hdr(info->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = dlen;
break;
case RECV_WAIT_SCO_HEADER:
- sh = (struct hci_sco_hdr *)(info->rx_skb->data);
+ sh = hci_sco_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = sh->dlen;
break;
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index 9fca6513562d..e8ebd5d3de86 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -231,7 +231,7 @@ static void bpa10x_wakeup(struct bpa10x_data *data)
cr = (struct usb_ctrlrequest *) urb->setup_packet;
cr->wLength = __cpu_to_le16(skb->len);
- memcpy(urb->transfer_buffer, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len);
urb->transfer_buffer_length = skb->len;
err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -250,7 +250,7 @@ static void bpa10x_wakeup(struct bpa10x_data *data)
skb = skb_dequeue(&data->tx_queue);
if (skb) {
- memcpy(urb->transfer_buffer, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len);
urb->transfer_buffer_length = skb->len;
err = usb_submit_urb(urb, GFP_ATOMIC);
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 18b0f3992c5b..39516074636b 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -303,20 +303,20 @@ static void bt3c_receive(bt3c_info_t *info)
switch (info->rx_state) {
case RECV_WAIT_EVENT_HEADER:
- eh = (struct hci_event_hdr *)(info->rx_skb->data);
+ eh = hci_event_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = eh->plen;
break;
case RECV_WAIT_ACL_HEADER:
- ah = (struct hci_acl_hdr *)(info->rx_skb->data);
+ ah = hci_acl_hdr(info->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = dlen;
break;
case RECV_WAIT_SCO_HEADER:
- sh = (struct hci_sco_hdr *)(info->rx_skb->data);
+ sh = hci_sco_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = sh->dlen;
break;
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index c1bce75148fe..d7d2ea0d86a1 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -250,20 +250,20 @@ static void btuart_receive(btuart_info_t *info)
switch (info->rx_state) {
case RECV_WAIT_EVENT_HEADER:
- eh = (struct hci_event_hdr *)(info->rx_skb->data);
+ eh = hci_event_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = eh->plen;
break;
case RECV_WAIT_ACL_HEADER:
- ah = (struct hci_acl_hdr *)(info->rx_skb->data);
+ ah = hci_acl_hdr(info->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = dlen;
break;
case RECV_WAIT_SCO_HEADER:
- sh = (struct hci_sco_hdr *)(info->rx_skb->data);
+ sh = hci_sco_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = sh->dlen;
break;
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 459aa97937ab..7f9c54b9964a 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -425,7 +425,7 @@ static int dtl1_hci_send_frame(struct sk_buff *skb)
return -ENOMEM;
skb_reserve(s, NSHL);
- memcpy(skb_put(s, skb->len), skb->data, skb->len);
+ skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len);
if (skb->len & 0x0001)
*skb_put(s, 1) = 0; /* PAD */
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index 34f0afc42407..bfbae14cf93d 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -188,7 +188,7 @@ static int h4_recv(struct hci_uart *hu, void *data, int count)
continue;
case H4_W4_EVENT_HDR:
- eh = (struct hci_event_hdr *) h4->rx_skb->data;
+ eh = hci_event_hdr(h4->rx_skb);
BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
@@ -196,7 +196,7 @@ static int h4_recv(struct hci_uart *hu, void *data, int count)
continue;
case H4_W4_ACL_HDR:
- ah = (struct hci_acl_hdr *) h4->rx_skb->data;
+ ah = hci_acl_hdr(h4->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
BT_DBG("ACL header: dlen %d", dlen);
@@ -205,7 +205,7 @@ static int h4_recv(struct hci_uart *hu, void *data, int count)
continue;
case H4_W4_SCO_HDR:
- sh = (struct hci_sco_hdr *) h4->rx_skb->data;
+ sh = hci_sco_hdr(h4->rx_skb);
BT_DBG("SCO header: dlen %d", sh->dlen);
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index f5c160caf9f4..5f066963f171 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -248,7 +248,7 @@ static unsigned long get_unmapped_area_mem(struct file *file,
{
if (!valid_mmap_phys_addr_range(pgoff, len))
return (unsigned long) -EINVAL;
- return pgoff;
+ return pgoff << PAGE_SHIFT;
}
/* can't do an in-place private mapping if there's no MMU */
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index a61fb6da5d03..80a01150b86c 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -1338,43 +1338,23 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int c
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
- case TIOCMIWAIT: {
- DECLARE_WAITQUEUE(wait, current);
- int ret;
+ case TIOCMIWAIT:
+ spin_lock_irqsave(&info->slock, flags);
+ cnow = info->icount; /* note the counters on entry */
+ spin_unlock_irqrestore(&info->slock, flags);
+
+ wait_event_interruptible(info->delta_msr_wait, ({
+ cprev = cnow;
spin_lock_irqsave(&info->slock, flags);
- cprev = info->icount; /* note the counters on entry */
+ cnow = info->icount; /* atomic copy */
spin_unlock_irqrestore(&info->slock, flags);
- add_wait_queue(&info->delta_msr_wait, &wait);
- while (1) {
- spin_lock_irqsave(&info->slock, flags);
- cnow = info->icount; /* atomic copy */
- spin_unlock_irqrestore(&info->slock, flags);
-
- set_current_state(TASK_INTERRUPTIBLE);
- 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))) {
- ret = 0;
- break;
- }
- /* see if a signal did it */
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- cprev = cnow;
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->delta_msr_wait, &wait);
- break;
- }
- /* NOTREACHED */
+ ((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));
+ }));
+ break;
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
diff --git a/drivers/char/mxser_new.c b/drivers/char/mxser_new.c
index 9af07e4999d5..f7603b6aeb87 100644
--- a/drivers/char/mxser_new.c
+++ b/drivers/char/mxser_new.c
@@ -1758,43 +1758,23 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
- case TIOCMIWAIT: {
- DECLARE_WAITQUEUE(wait, current);
- int ret;
+ case TIOCMIWAIT:
spin_lock_irqsave(&info->slock, flags);
- cprev = info->icount; /* note the counters on entry */
+ cnow = info->icount; /* note the counters on entry */
spin_unlock_irqrestore(&info->slock, flags);
- add_wait_queue(&info->delta_msr_wait, &wait);
- while (1) {
+ wait_event_interruptible(info->delta_msr_wait, ({
+ cprev = cnow;
spin_lock_irqsave(&info->slock, flags);
cnow = info->icount; /* atomic copy */
spin_unlock_irqrestore(&info->slock, flags);
- set_current_state(TASK_INTERRUPTIBLE);
- 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))) {
- ret = 0;
- break;
- }
- /* see if a signal did it */
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- cprev = cnow;
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->delta_msr_wait, &wait);
+ ((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));
+ }));
break;
- }
- /* NOTREACHED */
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
@@ -2230,7 +2210,14 @@ end_intr:
port->mon_data.rxcnt += cnt;
port->mon_data.up_rxcnt += cnt;
+ /*
+ * We are called from an interrupt context with &port->slock
+ * being held. Drop it temporarily in order to prevent
+ * recursive locking.
+ */
+ spin_unlock(&port->slock);
tty_flip_buffer_push(tty);
+ spin_lock(&port->slock);
}
static void mxser_transmit_chars(struct mxser_port *port)
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index 8d025e9b5bce..157b1d09ab55 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -4169,7 +4169,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
netif_stop_queue(dev);
/* copy data to device buffers */
- memcpy(info->tx_buf, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, info->tx_buf, skb->len);
info->tx_get = 0;
info->tx_put = info->tx_count = skb->len;
diff --git a/drivers/char/random.c b/drivers/char/random.c
index b9dc7aa1dfb3..46c1b97748b6 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -881,15 +881,15 @@ EXPORT_SYMBOL(get_random_bytes);
*/
static void init_std_data(struct entropy_store *r)
{
- struct timeval tv;
+ ktime_t now;
unsigned long flags;
spin_lock_irqsave(&r->lock, flags);
r->entropy_count = 0;
spin_unlock_irqrestore(&r->lock, flags);
- do_gettimeofday(&tv);
- add_entropy_words(r, (__u32 *)&tv, sizeof(tv)/4);
+ now = ktime_get_real();
+ add_entropy_words(r, (__u32 *)&now, sizeof(now)/4);
add_entropy_words(r, (__u32 *)utsname(),
sizeof(*(utsname()))/4);
}
@@ -911,14 +911,12 @@ void rand_initialize_irq(int irq)
return;
/*
- * If kmalloc returns null, we just won't use that entropy
+ * If kzalloc returns null, we just won't use that entropy
* source.
*/
- state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
- if (state) {
- memset(state, 0, sizeof(struct timer_rand_state));
+ state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
+ if (state)
irq_timer_state[irq] = state;
- }
}
#ifdef CONFIG_BLOCK
@@ -927,14 +925,12 @@ void rand_initialize_disk(struct gendisk *disk)
struct timer_rand_state *state;
/*
- * If kmalloc returns null, we just won't use that entropy
+ * If kzalloc returns null, we just won't use that entropy
* source.
*/
- state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
- if (state) {
- memset(state, 0, sizeof(struct timer_rand_state));
+ state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
+ if (state)
disk->random = state;
- }
}
#endif
@@ -1469,7 +1465,6 @@ late_initcall(seqgen_init);
__u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr,
__be16 sport, __be16 dport)
{
- struct timeval tv;
__u32 seq;
__u32 hash[12];
struct keydata *keyptr = get_keyptr();
@@ -1485,8 +1480,7 @@ __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr,
seq = twothirdsMD4Transform((const __u32 *)daddr, hash) & HASH_MASK;
seq += keyptr->count;
- do_gettimeofday(&tv);
- seq += tv.tv_usec + tv.tv_sec * 1000000;
+ seq += ktime_get_real().tv64;
return seq;
}
@@ -1521,7 +1515,6 @@ __u32 secure_ip_id(__be32 daddr)
__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
__be16 sport, __be16 dport)
{
- struct timeval tv;
__u32 seq;
__u32 hash[4];
struct keydata *keyptr = get_keyptr();
@@ -1543,12 +1536,11 @@ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
* As close as possible to RFC 793, which
* suggests using a 250 kHz clock.
* Further reading shows this assumes 2 Mb/s networks.
- * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate.
+ * For 10 Gb/s Ethernet, a 1 GHz clock is appropriate.
* That's funny, Linux has one built in! Use it!
* (Networks are faster now - should this be increased?)
*/
- do_gettimeofday(&tv);
- seq += tv.tv_usec + tv.tv_sec * 1000000;
+ seq += ktime_get_real().tv64;
#if 0
printk("init_seq(%lx, %lx, %d, %d) = %d\n",
saddr, daddr, sport, dport, seq);
@@ -1556,8 +1548,6 @@ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
return seq;
}
-EXPORT_SYMBOL(secure_tcp_sequence_number);
-
/* Generate secure starting point for ephemeral IPV4 transport port search */
u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
{
@@ -1598,7 +1588,6 @@ u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, __be16
u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
__be16 sport, __be16 dport)
{
- struct timeval tv;
u64 seq;
__u32 hash[4];
struct keydata *keyptr = get_keyptr();
@@ -1611,8 +1600,7 @@ u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
seq = half_md4_transform(hash, keyptr->secret);
seq |= ((u64)keyptr->count) << (32 - HASH_BITS);
- do_gettimeofday(&tv);
- seq += tv.tv_usec + tv.tv_sec * 1000000;
+ seq += ktime_get_real().tv64;
seq &= (1ull << 48) - 1;
#if 0
printk("dccp init_seq(%lx, %lx, %d, %d) = %d\n",
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index a905f7820331..a7b9e9bb3e8d 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -212,7 +212,7 @@ static void cn_rx_skb(struct sk_buff *__skb)
skb = skb_get(__skb);
if (skb->len >= NLMSG_SPACE(0)) {
- nlh = (struct nlmsghdr *)skb->data;
+ nlh = nlmsg_hdr(skb);
if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
skb->len < nlh->nlmsg_len ||
@@ -448,7 +448,7 @@ static int __devinit cn_init(void)
dev->nls = netlink_kernel_create(NETLINK_CONNECTOR,
CN_NETLINK_USERS + 0xf,
- dev->input, THIS_MODULE);
+ dev->input, NULL, THIS_MODULE);
if (!dev->nls)
return -EIO;
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index da5828f2dfc2..30a76404f0af 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -121,9 +121,9 @@ superio_exit(void)
* ISA constants
*/
-#define REGION_ALIGNMENT ~7
-#define REGION_OFFSET 5
-#define REGION_LENGTH 2
+#define IOREGION_ALIGNMENT ~7
+#define IOREGION_OFFSET 5
+#define IOREGION_LENGTH 2
#define ADDR_REG_OFFSET 5
#define DATA_REG_OFFSET 6
@@ -407,7 +407,7 @@ static void w83627ehf_write_fan_div(struct i2c_client *client, int nr)
break;
case 4:
reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73)
- | ((data->fan_div[4] & 0x03) << 3)
+ | ((data->fan_div[4] & 0x03) << 2)
| ((data->fan_div[4] & 0x04) << 5);
w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
break;
@@ -471,9 +471,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
time */
if (data->fan[i] == 0xff
&& data->fan_div[i] < 0x07) {
- dev_dbg(&client->dev, "Increasing fan %d "
+ dev_dbg(&client->dev, "Increasing fan%d "
"clock divider from %u to %u\n",
- i, div_from_reg(data->fan_div[i]),
+ i + 1, div_from_reg(data->fan_div[i]),
div_from_reg(data->fan_div[i] + 1));
data->fan_div[i]++;
w83627ehf_write_fan_div(client, i);
@@ -1194,7 +1194,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
u8 fan4pin, fan5pin;
int i, err = 0;
- if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
+ if (!request_region(address + IOREGION_OFFSET, IOREGION_LENGTH,
w83627ehf_driver.driver.name)) {
err = -EBUSY;
goto exit;
@@ -1322,7 +1322,7 @@ exit_remove:
exit_free:
kfree(data);
exit_release:
- release_region(address + REGION_OFFSET, REGION_LENGTH);
+ release_region(address + IOREGION_OFFSET, IOREGION_LENGTH);
exit:
return err;
}
@@ -1337,7 +1337,7 @@ static int w83627ehf_detach_client(struct i2c_client *client)
if ((err = i2c_detach_client(client)))
return err;
- release_region(client->addr + REGION_OFFSET, REGION_LENGTH);
+ release_region(client->addr + IOREGION_OFFSET, IOREGION_LENGTH);
kfree(data);
return 0;
@@ -1380,7 +1380,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr)
superio_select(W83627EHF_LD_HWM);
val = (superio_inb(SIO_REG_ADDR) << 8)
| superio_inb(SIO_REG_ADDR + 1);
- *addr = val & REGION_ALIGNMENT;
+ *addr = val & IOREGION_ALIGNMENT;
if (*addr == 0) {
superio_exit();
return -ENODEV;
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index fb19dbb31e42..ece31d2c6c64 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -344,8 +344,7 @@ config I2C_PARPORT_LIGHT
config I2C_PASEMI
tristate "PA Semi SMBus interface"
-# depends on PPC_PASEMI && I2C && PCI
- depends on I2C && PCI
+ depends on PPC_PASEMI && I2C && PCI
help
Supports the PA Semi PWRficient on-chip SMBus interfaces.
diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c
index f54fb5d65cc4..bf89eeef74e9 100644
--- a/drivers/i2c/busses/i2c-pasemi.c
+++ b/drivers/i2c/busses/i2c-pasemi.c
@@ -141,7 +141,7 @@ static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter,
for (i = 0; i < msg->len - 1; i++)
TXFIFO_WR(smbus, msg->buf[i]);
- TXFIFO_WR(smbus, msg->buf[msg->len] |
+ TXFIFO_WR(smbus, msg->buf[msg->len-1] |
(stop ? MTXFIFO_STOP : 0));
}
@@ -226,7 +226,7 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter,
rd = RXFIFO_RD(smbus);
len = min_t(u8, (rd & MRXFIFO_DATA_M),
I2C_SMBUS_BLOCK_MAX);
- TXFIFO_WR(smbus, (len + 1) | MTXFIFO_READ |
+ TXFIFO_WR(smbus, len | MTXFIFO_READ |
MTXFIFO_STOP);
} else {
len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX);
@@ -258,7 +258,7 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter,
rd = RXFIFO_RD(smbus);
len = min_t(u8, (rd & MRXFIFO_DATA_M),
I2C_SMBUS_BLOCK_MAX - len);
- TXFIFO_WR(smbus, (len + 1) | MTXFIFO_READ | MTXFIFO_STOP);
+ TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP);
break;
default:
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index ca2e4f830c39..5bdf64b77913 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -57,6 +57,7 @@ if IDE
config IDE_MAX_HWIFS
int "Max IDE interfaces"
depends on ALPHA || SUPERH || IA64 || EMBEDDED
+ range 1 10
default 4
help
This is the maximum number of IDE hardware interfaces that will
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index afb71c66b6f3..a9e0b30fb1f2 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -310,14 +310,12 @@ static int proc_ide_read_driver
ide_driver_t *ide_drv;
int len;
- down_read(&dev->bus->subsys.rwsem);
if (dev->driver) {
ide_drv = container_of(dev->driver, ide_driver_t, gen_driver);
len = sprintf(page, "%s version %s\n",
dev->driver->name, ide_drv->version);
} else
len = sprintf(page, "ide-default version 0.9.newide\n");
- up_read(&dev->bus->subsys.rwsem);
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
}
@@ -327,7 +325,6 @@ static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
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));
@@ -345,7 +342,6 @@ static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
}
if (dev->driver && !strcmp(dev->driver->name, driver))
ret = 0;
- up_write(&dev->bus->subsys.rwsem);
return ret;
}
diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c
index d4b753e70119..dd7ec37fdeab 100644
--- a/drivers/ide/pci/delkin_cb.c
+++ b/drivers/ide/pci/delkin_cb.c
@@ -108,6 +108,7 @@ delkin_cb_remove (struct pci_dev *dev)
static struct pci_device_id delkin_cb_pci_tbl[] __devinitdata = {
{ 0x1145, 0xf021, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { 0x1145, 0xf024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, delkin_cb_pci_tbl);
diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c
index 60ecdc258c7c..ab6fa271aeb3 100644
--- a/drivers/ide/pci/hpt366.c
+++ b/drivers/ide/pci/hpt366.c
@@ -1,10 +1,10 @@
/*
- * linux/drivers/ide/pci/hpt366.c Version 1.01 Dec 23, 2006
+ * linux/drivers/ide/pci/hpt366.c Version 1.02 Apr 18, 2007
*
* Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org>
* Portions Copyright (C) 2001 Sun Microsystems, Inc.
* Portions Copyright (C) 2003 Red Hat Inc
- * Portions Copyright (C) 2005-2006 MontaVista Software, Inc.
+ * Portions Copyright (C) 2005-2007 MontaVista Software, Inc.
*
* Thanks to HighPoint Technologies for their assistance, and hardware.
* Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his
@@ -494,6 +494,7 @@ static struct hpt_info hpt302n __devinitdata = {
.chip_type = HPT302N,
.max_mode = HPT302_ALLOW_ATA133_6 ? 4 : 3,
.dpll_clk = 77,
+ .settings = hpt37x_settings
};
static struct hpt_info hpt371n __devinitdata = {
diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c
index 03e44b337eb0..a364003ba47f 100644
--- a/drivers/ieee1394/eth1394.c
+++ b/drivers/ieee1394/eth1394.c
@@ -834,7 +834,7 @@ static inline u16 ether1394_type_trans(struct sk_buff *skb,
struct eth1394hdr *eth;
unsigned char *rawp;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull (skb, ETH1394_HLEN);
eth = eth1394_hdr(skb);
@@ -1668,7 +1668,7 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev)
if (memcmp(eth->h_dest, dev->broadcast, ETH1394_ALEN) == 0 ||
proto == htons(ETH_P_ARP) ||
(proto == htons(ETH_P_IP) &&
- IN_MULTICAST(ntohl(skb->nh.iph->daddr)))) {
+ IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) {
tx_type = ETH1394_GASP;
dest_node = LOCAL_BUS | ALL_NODES;
max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD;
diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h
index c45cbff9138d..1e8356535149 100644
--- a/drivers/ieee1394/eth1394.h
+++ b/drivers/ieee1394/eth1394.h
@@ -90,7 +90,7 @@ struct eth1394hdr {
static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb)
{
- return (struct eth1394hdr *)skb->mac.raw;
+ return (struct eth1394hdr *)skb_mac_header(skb);
}
#endif
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index c5ace190bfe6..dbeba45a031e 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -370,9 +370,7 @@ static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute
if (state == 1) {
ud->ignore_driver = 1;
- down_write(&ieee1394_bus_type.subsys.rwsem);
device_release_driver(dev);
- up_write(&ieee1394_bus_type.subsys.rwsem);
} else if (state == 0)
ud->ignore_driver = 0;
@@ -1163,6 +1161,7 @@ static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp,
struct unit_directory *ud;
int i = 0;
int length = 0;
+ int retval = 0;
/* ieee1394:venNmoNspNverN */
char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1];
@@ -1176,14 +1175,11 @@ static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp,
#define PUT_ENVP(fmt,val) \
do { \
- int printed; \
- envp[i++] = buffer; \
- printed = snprintf(buffer, buffer_size - length, \
- fmt, val); \
- if ((buffer_size - (length+printed) <= 0) || (i >= num_envp)) \
- return -ENOMEM; \
- length += printed+1; \
- buffer += printed+1; \
+ retval = add_uevent_var(envp, num_envp, &i, \
+ buffer, buffer_size, &length, \
+ fmt, val); \
+ if (retval) \
+ return retval; \
} while (0)
PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id);
@@ -1393,12 +1389,10 @@ static void nodemgr_suspend_ne(struct node_entry *ne)
if (ud->ne != ne)
continue;
- down_write(&ieee1394_bus_type.subsys.rwsem);
if (ud->device.driver &&
(!ud->device.driver->suspend ||
ud->device.driver->suspend(&ud->device, PMSG_SUSPEND)))
device_release_driver(&ud->device);
- up_write(&ieee1394_bus_type.subsys.rwsem);
}
up(&nodemgr_ud_class.sem);
}
@@ -1418,10 +1412,8 @@ static void nodemgr_resume_ne(struct node_entry *ne)
if (ud->ne != ne)
continue;
- down_read(&ieee1394_bus_type.subsys.rwsem);
if (ud->device.driver && ud->device.driver->resume)
ud->device.driver->resume(&ud->device);
- up_read(&ieee1394_bus_type.subsys.rwsem);
}
up(&nodemgr_ud_class.sem);
@@ -1442,7 +1434,6 @@ static void nodemgr_update_pdrv(struct node_entry *ne)
if (ud->ne != ne)
continue;
- down_write(&ieee1394_bus_type.subsys.rwsem);
if (ud->device.driver) {
pdrv = container_of(ud->device.driver,
struct hpsb_protocol_driver,
@@ -1450,7 +1441,6 @@ static void nodemgr_update_pdrv(struct node_entry *ne)
if (pdrv->update && pdrv->update(ud))
device_release_driver(&ud->device);
}
- up_write(&ieee1394_bus_type.subsys.rwsem);
}
up(&nodemgr_ud_class.sem);
}
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 13efd4170349..6edfecf1be72 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2004-2007 Voltaire, Inc. All rights reserved.
* Copyright (c) 2005 Intel Corporation. All rights reserved.
* Copyright (c) 2005 Mellanox Technologies Ltd. All rights reserved.
*
@@ -31,7 +31,6 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
- * $Id: mad.c 5596 2006-03-03 01:00:07Z sean.hefty $
*/
#include <linux/dma-mapping.h>
#include <rdma/ib_cache.h>
@@ -668,7 +667,7 @@ static void build_smp_wc(struct ib_qp *qp,
static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
struct ib_mad_send_wr_private *mad_send_wr)
{
- int ret;
+ int ret = 0;
struct ib_smp *smp = mad_send_wr->send_buf.mad;
unsigned long flags;
struct ib_mad_local_private *local;
@@ -688,14 +687,15 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
*/
if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) ==
IB_LID_PERMISSIVE &&
- !smi_handle_dr_smp_send(smp, device->node_type, port_num)) {
+ smi_handle_dr_smp_send(smp, device->node_type, port_num) ==
+ IB_SMI_DISCARD) {
ret = -EINVAL;
printk(KERN_ERR PFX "Invalid directed route\n");
goto out;
}
+
/* Check to post send on QP or process locally */
- ret = smi_check_local_smp(smp, device);
- if (!ret)
+ if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD)
goto out;
local = kmalloc(sizeof *local, GFP_ATOMIC);
@@ -1874,18 +1874,22 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
if (recv->mad.mad.mad_hdr.mgmt_class ==
IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
- if (!smi_handle_dr_smp_recv(&recv->mad.smp,
- port_priv->device->node_type,
- port_priv->port_num,
- port_priv->device->phys_port_cnt))
+ if (smi_handle_dr_smp_recv(&recv->mad.smp,
+ port_priv->device->node_type,
+ port_priv->port_num,
+ port_priv->device->phys_port_cnt) ==
+ IB_SMI_DISCARD)
goto out;
- if (!smi_check_forward_dr_smp(&recv->mad.smp))
+
+ if (smi_check_forward_dr_smp(&recv->mad.smp) == IB_SMI_LOCAL)
goto local;
- if (!smi_handle_dr_smp_send(&recv->mad.smp,
- port_priv->device->node_type,
- port_priv->port_num))
+
+ if (smi_handle_dr_smp_send(&recv->mad.smp,
+ port_priv->device->node_type,
+ port_priv->port_num) == IB_SMI_DISCARD)
goto out;
- if (!smi_check_local_smp(&recv->mad.smp, port_priv->device))
+
+ if (smi_check_local_smp(&recv->mad.smp, port_priv->device) == IB_SMI_DISCARD)
goto out;
}
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index 68db633711c5..9a7eaadb1688 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -57,6 +57,7 @@ MODULE_LICENSE("Dual BSD/GPL");
struct ib_sa_sm_ah {
struct ib_ah *ah;
struct kref ref;
+ u8 src_path_mask;
};
struct ib_sa_port {
@@ -380,6 +381,7 @@ static void update_sm_ah(struct work_struct *work)
}
kref_init(&new_ah->ref);
+ new_ah->src_path_mask = (1 << port_attr.lmc) - 1;
memset(&ah_attr, 0, sizeof ah_attr);
ah_attr.dlid = port_attr.sm_lid;
@@ -460,6 +462,25 @@ void ib_sa_cancel_query(int id, struct ib_sa_query *query)
}
EXPORT_SYMBOL(ib_sa_cancel_query);
+static u8 get_src_path_mask(struct ib_device *device, u8 port_num)
+{
+ struct ib_sa_device *sa_dev;
+ struct ib_sa_port *port;
+ unsigned long flags;
+ u8 src_path_mask;
+
+ sa_dev = ib_get_client_data(device, &sa_client);
+ if (!sa_dev)
+ return 0x7f;
+
+ port = &sa_dev->port[port_num - sa_dev->start_port];
+ spin_lock_irqsave(&port->ah_lock, flags);
+ src_path_mask = port->sm_ah ? port->sm_ah->src_path_mask : 0x7f;
+ spin_unlock_irqrestore(&port->ah_lock, flags);
+
+ return src_path_mask;
+}
+
int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
struct ib_sa_path_rec *rec, struct ib_ah_attr *ah_attr)
{
@@ -469,7 +490,8 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
memset(ah_attr, 0, sizeof *ah_attr);
ah_attr->dlid = be16_to_cpu(rec->dlid);
ah_attr->sl = rec->sl;
- ah_attr->src_path_bits = be16_to_cpu(rec->slid) & 0x7f;
+ ah_attr->src_path_bits = be16_to_cpu(rec->slid) &
+ get_src_path_mask(device, port_num);
ah_attr->port_num = port_num;
ah_attr->static_rate = rec->rate;
diff --git a/drivers/infiniband/core/smi.c b/drivers/infiniband/core/smi.c
index 54b81e17ad50..2bca753eb622 100644
--- a/drivers/infiniband/core/smi.c
+++ b/drivers/infiniband/core/smi.c
@@ -3,7 +3,7 @@
* Copyright (c) 2004, 2005 Infinicon Corporation. All rights reserved.
* Copyright (c) 2004, 2005 Intel Corporation. All rights reserved.
* Copyright (c) 2004, 2005 Topspin Corporation. All rights reserved.
- * Copyright (c) 2004, 2005 Voltaire Corporation. All rights reserved.
+ * Copyright (c) 2004-2007 Voltaire Corporation. All rights reserved.
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
@@ -34,7 +34,6 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
- * $Id: smi.c 1389 2004-12-27 22:56:47Z roland $
*/
#include <rdma/ib_smi.h>
@@ -44,9 +43,8 @@
* Fixup a directed route SMP for sending
* Return 0 if the SMP should be discarded
*/
-int smi_handle_dr_smp_send(struct ib_smp *smp,
- u8 node_type,
- int port_num)
+enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
+ u8 node_type, int port_num)
{
u8 hop_ptr, hop_cnt;
@@ -59,18 +57,18 @@ int smi_handle_dr_smp_send(struct ib_smp *smp,
if (hop_cnt && hop_ptr == 0) {
smp->hop_ptr++;
return (smp->initial_path[smp->hop_ptr] ==
- port_num);
+ port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:2 */
if (hop_ptr && hop_ptr < hop_cnt) {
if (node_type != RDMA_NODE_IB_SWITCH)
- return 0;
+ return IB_SMI_DISCARD;
/* smp->return_path set when received */
smp->hop_ptr++;
return (smp->initial_path[smp->hop_ptr] ==
- port_num);
+ port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:3 -- We're at the end of the DR segment of path */
@@ -78,29 +76,30 @@ int smi_handle_dr_smp_send(struct ib_smp *smp,
/* smp->return_path set when received */
smp->hop_ptr++;
return (node_type == RDMA_NODE_IB_SWITCH ||
- smp->dr_dlid == IB_LID_PERMISSIVE);
+ smp->dr_dlid == IB_LID_PERMISSIVE ?
+ IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
/* C14-9:5 -- Fail unreasonable hop pointer */
- return (hop_ptr == hop_cnt + 1);
+ return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
} else {
/* C14-13:1 */
if (hop_cnt && hop_ptr == hop_cnt + 1) {
smp->hop_ptr--;
return (smp->return_path[smp->hop_ptr] ==
- port_num);
+ port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:2 */
if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
if (node_type != RDMA_NODE_IB_SWITCH)
- return 0;
+ return IB_SMI_DISCARD;
smp->hop_ptr--;
return (smp->return_path[smp->hop_ptr] ==
- port_num);
+ port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:3 -- at the end of the DR segment of path */
@@ -108,15 +107,16 @@ int smi_handle_dr_smp_send(struct ib_smp *smp,
smp->hop_ptr--;
/* C14-13:3 -- SMPs destined for SM shouldn't be here */
return (node_type == RDMA_NODE_IB_SWITCH ||
- smp->dr_slid == IB_LID_PERMISSIVE);
+ smp->dr_slid == IB_LID_PERMISSIVE ?
+ IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */
if (hop_ptr == 0)
- return 1;
+ return IB_SMI_HANDLE;
/* C14-13:5 -- Check for unreasonable hop pointer */
- return 0;
+ return IB_SMI_DISCARD;
}
}
@@ -124,10 +124,8 @@ int smi_handle_dr_smp_send(struct ib_smp *smp,
* Adjust information for a received SMP
* Return 0 if the SMP should be dropped
*/
-int smi_handle_dr_smp_recv(struct ib_smp *smp,
- u8 node_type,
- int port_num,
- int phys_port_cnt)
+enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
+ int port_num, int phys_port_cnt)
{
u8 hop_ptr, hop_cnt;
@@ -138,16 +136,17 @@ int smi_handle_dr_smp_recv(struct ib_smp *smp,
if (!ib_get_smp_direction(smp)) {
/* C14-9:1 -- sender should have incremented hop_ptr */
if (hop_cnt && hop_ptr == 0)
- return 0;
+ return IB_SMI_DISCARD;
/* C14-9:2 -- intermediate hop */
if (hop_ptr && hop_ptr < hop_cnt) {
if (node_type != RDMA_NODE_IB_SWITCH)
- return 0;
+ return IB_SMI_DISCARD;
smp->return_path[hop_ptr] = port_num;
/* smp->hop_ptr updated when sending */
- return (smp->initial_path[hop_ptr+1] <= phys_port_cnt);
+ return (smp->initial_path[hop_ptr+1] <= phys_port_cnt ?
+ IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:3 -- We're at the end of the DR segment of path */
@@ -157,12 +156,13 @@ int smi_handle_dr_smp_recv(struct ib_smp *smp,
/* smp->hop_ptr updated when sending */
return (node_type == RDMA_NODE_IB_SWITCH ||
- smp->dr_dlid == IB_LID_PERMISSIVE);
+ smp->dr_dlid == IB_LID_PERMISSIVE ?
+ IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
/* C14-9:5 -- fail unreasonable hop pointer */
- return (hop_ptr == hop_cnt + 1);
+ return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
} else {
@@ -170,16 +170,17 @@ int smi_handle_dr_smp_recv(struct ib_smp *smp,
if (hop_cnt && hop_ptr == hop_cnt + 1) {
smp->hop_ptr--;
return (smp->return_path[smp->hop_ptr] ==
- port_num);
+ port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:2 */
if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
if (node_type != RDMA_NODE_IB_SWITCH)
- return 0;
+ return IB_SMI_DISCARD;
/* smp->hop_ptr updated when sending */
- return (smp->return_path[hop_ptr-1] <= phys_port_cnt);
+ return (smp->return_path[hop_ptr-1] <= phys_port_cnt ?
+ IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:3 -- We're at the end of the DR segment of path */
@@ -187,23 +188,20 @@ int smi_handle_dr_smp_recv(struct ib_smp *smp,
if (smp->dr_slid == IB_LID_PERMISSIVE) {
/* giving SMP to SM - update hop_ptr */
smp->hop_ptr--;
- return 1;
+ return IB_SMI_HANDLE;
}
/* smp->hop_ptr updated when sending */
- return (node_type == RDMA_NODE_IB_SWITCH);
+ return (node_type == RDMA_NODE_IB_SWITCH ?
+ IB_SMI_HANDLE: IB_SMI_DISCARD);
}
/* C14-13:4 -- hop_ptr = 0 -> give to SM */
/* C14-13:5 -- Check for unreasonable hop pointer */
- return (hop_ptr == 0);
+ return (hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
}
-/*
- * Return 1 if the received DR SMP should be forwarded to the send queue
- * Return 0 if the SMP should be completed up the stack
- */
-int smi_check_forward_dr_smp(struct ib_smp *smp)
+enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
{
u8 hop_ptr, hop_cnt;
@@ -213,23 +211,25 @@ int smi_check_forward_dr_smp(struct ib_smp *smp)
if (!ib_get_smp_direction(smp)) {
/* C14-9:2 -- intermediate hop */
if (hop_ptr && hop_ptr < hop_cnt)
- return 1;
+ return IB_SMI_SEND;
/* C14-9:3 -- at the end of the DR segment of path */
if (hop_ptr == hop_cnt)
- return (smp->dr_dlid == IB_LID_PERMISSIVE);
+ return (smp->dr_dlid == IB_LID_PERMISSIVE ?
+ IB_SMI_SEND : IB_SMI_LOCAL);
/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
if (hop_ptr == hop_cnt + 1)
- return 1;
+ return IB_SMI_SEND;
} else {
- /* C14-13:2 */
+ /* C14-13:2 -- intermediate hop */
if (2 <= hop_ptr && hop_ptr <= hop_cnt)
- return 1;
+ return IB_SMI_SEND;
/* C14-13:3 -- at the end of the DR segment of path */
if (hop_ptr == 1)
- return (smp->dr_slid != IB_LID_PERMISSIVE);
+ return (smp->dr_slid != IB_LID_PERMISSIVE ?
+ IB_SMI_SEND : IB_SMI_LOCAL);
}
- return 0;
+ return IB_SMI_LOCAL;
}
diff --git a/drivers/infiniband/core/smi.h b/drivers/infiniband/core/smi.h
index 3011bfd86dc5..9a4b349efc30 100644
--- a/drivers/infiniband/core/smi.h
+++ b/drivers/infiniband/core/smi.h
@@ -3,7 +3,7 @@
* Copyright (c) 2004 Infinicon Corporation. All rights reserved.
* Copyright (c) 2004 Intel Corporation. All rights reserved.
* Copyright (c) 2004 Topspin Corporation. All rights reserved.
- * Copyright (c) 2004 Voltaire Corporation. All rights reserved.
+ * Copyright (c) 2004-2007 Voltaire Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -33,7 +33,6 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
- * $Id: smi.h 1389 2004-12-27 22:56:47Z roland $
*/
#ifndef __SMI_H_
@@ -41,26 +40,33 @@
#include <rdma/ib_smi.h>
-int smi_handle_dr_smp_recv(struct ib_smp *smp,
- u8 node_type,
- int port_num,
- int phys_port_cnt);
-extern int smi_check_forward_dr_smp(struct ib_smp *smp);
-extern int smi_handle_dr_smp_send(struct ib_smp *smp,
- u8 node_type,
- int port_num);
+enum smi_action {
+ IB_SMI_DISCARD,
+ IB_SMI_HANDLE
+};
+
+enum smi_forward_action {
+ IB_SMI_LOCAL, /* SMP should be completed up the stack */
+ IB_SMI_SEND, /* received DR SMP should be forwarded to the send queue */
+};
+
+enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
+ int port_num, int phys_port_cnt);
+extern enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp);
+extern enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
+ u8 node_type, int port_num);
/*
* Return 1 if the SMP should be handled by the local SMA/SM via process_mad
*/
-static inline int smi_check_local_smp(struct ib_smp *smp,
- struct ib_device *device)
+static inline enum smi_action smi_check_local_smp(struct ib_smp *smp,
+ struct ib_device *device)
{
/* C14-9:3 -- We're at the end of the DR segment of path */
/* C14-9:4 -- Hop Pointer = Hop Count + 1 -> give to SMA/SM */
return ((device->process_mad &&
!ib_get_smp_direction(smp) &&
- (smp->hop_ptr == smp->hop_cnt + 1)));
+ (smp->hop_ptr == smp->hop_cnt + 1)) ?
+ IB_SMI_HANDLE : IB_SMI_DISCARD);
}
-
#endif /* __SMI_H_ */
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 000c086bf2e9..08c299ebf4a8 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -683,6 +683,7 @@ int ib_device_register_sysfs(struct ib_device *device)
class_dev->class = &ib_class;
class_dev->class_data = device;
+ class_dev->dev = device->dma_device;
strlcpy(class_dev->class_id, device->name, BUS_ID_SIZE);
INIT_LIST_HEAD(&device->port_list);
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index ee51d79a7ad5..2586a3ee8eba 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -407,29 +407,18 @@ static ssize_t ib_ucm_event(struct ib_ucm_file *file,
mutex_lock(&file->file_mutex);
while (list_empty(&file->events)) {
+ mutex_unlock(&file->file_mutex);
- if (file->filp->f_flags & O_NONBLOCK) {
- result = -EAGAIN;
- break;
- }
+ if (file->filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
- if (signal_pending(current)) {
- result = -ERESTARTSYS;
- break;
- }
+ if (wait_event_interruptible(file->poll_wait,
+ !list_empty(&file->events)))
+ return -ERESTARTSYS;
- prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE);
-
- mutex_unlock(&file->file_mutex);
- schedule();
mutex_lock(&file->file_mutex);
-
- finish_wait(&file->poll_wait, &wait);
}
- if (result)
- goto done;
-
uevent = list_entry(file->events.next, struct ib_ucm_event, file_list);
if (ib_ucm_new_cm_id(uevent->resp.event)) {
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index c859134c1daa..53b4c94a7eb5 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -306,26 +306,18 @@ static ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf,
mutex_lock(&file->mut);
while (list_empty(&file->event_list)) {
- if (file->filp->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- break;
- }
+ mutex_unlock(&file->mut);
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
+ if (file->filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ if (wait_event_interruptible(file->poll_wait,
+ !list_empty(&file->event_list)))
+ return -ERESTARTSYS;
- prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE);
- mutex_unlock(&file->mut);
- schedule();
mutex_lock(&file->mut);
- finish_wait(&file->poll_wait, &wait);
}
- if (ret)
- goto done;
-
uevent = list_entry(file->event_list.next, struct ucma_event, list);
if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST) {
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index c069ebeba8e3..8199b83052a9 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -135,7 +135,7 @@ static const dev_t base_dev = MKDEV(IB_UMAD_MAJOR, IB_UMAD_MINOR_BASE);
static DEFINE_SPINLOCK(port_lock);
static struct ib_umad_port *umad_port[IB_UMAD_MAX_PORTS];
-static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS * 2);
+static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS);
static void ib_umad_add_one(struct ib_device *device);
static void ib_umad_remove_one(struct ib_device *device);
@@ -231,12 +231,17 @@ static void recv_handler(struct ib_mad_agent *agent,
packet->mad.hdr.path_bits = mad_recv_wc->wc->dlid_path_bits;
packet->mad.hdr.grh_present = !!(mad_recv_wc->wc->wc_flags & IB_WC_GRH);
if (packet->mad.hdr.grh_present) {
- /* XXX parse GRH */
- packet->mad.hdr.gid_index = 0;
- packet->mad.hdr.hop_limit = 0;
- packet->mad.hdr.traffic_class = 0;
- memset(packet->mad.hdr.gid, 0, 16);
- packet->mad.hdr.flow_label = 0;
+ struct ib_ah_attr ah_attr;
+
+ ib_init_ah_from_wc(agent->device, agent->port_num,
+ mad_recv_wc->wc, mad_recv_wc->recv_buf.grh,
+ &ah_attr);
+
+ packet->mad.hdr.gid_index = ah_attr.grh.sgid_index;
+ packet->mad.hdr.hop_limit = ah_attr.grh.hop_limit;
+ packet->mad.hdr.traffic_class = ah_attr.grh.traffic_class;
+ memcpy(packet->mad.hdr.gid, &ah_attr.grh.dgid, 16);
+ packet->mad.hdr.flow_label = cpu_to_be32(ah_attr.grh.flow_label);
}
if (queue_packet(file, agent, packet))
@@ -473,6 +478,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
if (packet->mad.hdr.grh_present) {
ah_attr.ah_flags = IB_AH_GRH;
memcpy(ah_attr.grh.dgid.raw, packet->mad.hdr.gid, 16);
+ ah_attr.grh.sgid_index = packet->mad.hdr.gid_index;
ah_attr.grh.flow_label = be32_to_cpu(packet->mad.hdr.flow_label);
ah_attr.grh.hop_limit = packet->mad.hdr.hop_limit;
ah_attr.grh.traffic_class = packet->mad.hdr.traffic_class;
diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c
index 59243d9aedd6..58bc272bd407 100644
--- a/drivers/infiniband/hw/amso1100/c2.c
+++ b/drivers/infiniband/hw/amso1100/c2.c
@@ -439,7 +439,8 @@ static void c2_rx_error(struct c2_port *c2_port, struct c2_element *elem)
}
/* Setup the skb for reuse since we're dropping this pkt */
- elem->skb->tail = elem->skb->data = elem->skb->head;
+ elem->skb->data = elem->skb->head;
+ skb_reset_tail_pointer(elem->skb);
/* Zero out the rxp hdr in the sk_buff */
memset(elem->skb->data, 0, sizeof(*rxp_hdr));
@@ -521,9 +522,8 @@ static void c2_rx_interrupt(struct net_device *netdev)
* "sizeof(struct c2_rxp_hdr)".
*/
skb->data += sizeof(*rxp_hdr);
- skb->tail = skb->data + buflen;
+ skb_set_tail_pointer(skb, buflen);
skb->len = buflen;
- skb->dev = netdev;
skb->protocol = eth_type_trans(skb, netdev);
netif_rx(skb);
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index fef972752912..607c09bf764c 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -796,7 +796,6 @@ int c2_register_device(struct c2_dev *dev)
memcpy(&dev->ibdev.node_guid, dev->pseudo_netdev->dev_addr, 6);
dev->ibdev.phys_port_cnt = 1;
dev->ibdev.dma_device = &dev->pcidev->dev;
- dev->ibdev.class_dev.dev = &dev->pcidev->dev;
dev->ibdev.query_device = c2_query_device;
dev->ibdev.query_port = c2_query_port;
dev->ibdev.modify_port = c2_modify_port;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index d0ed1d35ca3e..3b4b0acd707f 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -477,7 +477,7 @@ static void send_mpa_req(struct iwch_ep *ep, struct sk_buff *skb)
BUG_ON(skb_cloned(skb));
mpalen = sizeof(*mpa) + ep->plen;
- if (skb->data + mpalen + sizeof(*req) > skb->end) {
+ if (skb->data + mpalen + sizeof(*req) > skb_end_pointer(skb)) {
kfree_skb(skb);
skb=alloc_skb(mpalen + sizeof(*req), GFP_KERNEL);
if (!skb) {
@@ -507,7 +507,7 @@ static void send_mpa_req(struct iwch_ep *ep, struct sk_buff *skb)
*/
skb_get(skb);
set_arp_failure_handler(skb, arp_failure_discard);
- skb->h.raw = skb->data;
+ skb_reset_transport_header(skb);
len = skb->len;
req = (struct tx_data_wr *) skb_push(skb, sizeof(*req));
req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA));
@@ -559,7 +559,7 @@ static int send_mpa_reject(struct iwch_ep *ep, const void *pdata, u8 plen)
skb_get(skb);
skb->priority = CPL_PRIORITY_DATA;
set_arp_failure_handler(skb, arp_failure_discard);
- skb->h.raw = skb->data;
+ skb_reset_transport_header(skb);
req = (struct tx_data_wr *) skb_push(skb, sizeof(*req));
req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA));
req->wr_lo = htonl(V_WR_TID(ep->hwtid));
@@ -610,7 +610,7 @@ static int send_mpa_reply(struct iwch_ep *ep, const void *pdata, u8 plen)
*/
skb_get(skb);
set_arp_failure_handler(skb, arp_failure_discard);
- skb->h.raw = skb->data;
+ skb_reset_transport_header(skb);
len = skb->len;
req = (struct tx_data_wr *) skb_push(skb, sizeof(*req));
req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA));
@@ -821,7 +821,8 @@ static void process_mpa_reply(struct iwch_ep *ep, struct sk_buff *skb)
/*
* copy the new data into our accumulation buffer.
*/
- memcpy(&(ep->mpa_pkt[ep->mpa_pkt_len]), skb->data, skb->len);
+ skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]),
+ skb->len);
ep->mpa_pkt_len += skb->len;
/*
@@ -940,7 +941,8 @@ static void process_mpa_request(struct iwch_ep *ep, struct sk_buff *skb)
/*
* Copy the new data into our accumulation buffer.
*/
- memcpy(&(ep->mpa_pkt[ep->mpa_pkt_len]), skb->data, skb->len);
+ skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]),
+ skb->len);
ep->mpa_pkt_len += skb->len;
/*
@@ -1619,7 +1621,8 @@ static int terminate(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
PDBG("%s ep %p\n", __FUNCTION__, ep);
skb_pull(skb, sizeof(struct cpl_rdma_terminate));
PDBG("%s saving %d bytes of term msg\n", __FUNCTION__, skb->len);
- memcpy(ep->com.qp->attr.terminate_buffer, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, ep->com.qp->attr.terminate_buffer,
+ skb->len);
ep->com.qp->attr.terminate_msg_len = skb->len;
ep->com.qp->attr.is_terminate_local = 0;
return CPL_RET_BUF_DONE;
@@ -2026,6 +2029,17 @@ static int sched(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
return 0;
}
+static int set_tcb_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
+{
+ struct cpl_set_tcb_rpl *rpl = cplhdr(skb);
+
+ if (rpl->status != CPL_ERR_NONE) {
+ printk(KERN_ERR MOD "Unexpected SET_TCB_RPL status %u "
+ "for tid %u\n", rpl->status, GET_TID(rpl));
+ }
+ return CPL_RET_BUF_DONE;
+}
+
int __init iwch_cm_init(void)
{
skb_queue_head_init(&rxq);
@@ -2053,6 +2067,7 @@ int __init iwch_cm_init(void)
t3c_handlers[CPL_ABORT_REQ_RSS] = sched;
t3c_handlers[CPL_RDMA_TERMINATE] = sched;
t3c_handlers[CPL_RDMA_EC_STATUS] = sched;
+ t3c_handlers[CPL_SET_TCB_RPL] = set_tcb_rpl;
/*
* These are the real handlers that are called from a
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index 24e0df04f7db..af28a317016d 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -1108,7 +1108,6 @@ int iwch_register_device(struct iwch_dev *dev)
memcpy(dev->ibdev.node_desc, IWCH_NODE_DESC, sizeof(IWCH_NODE_DESC));
dev->ibdev.phys_port_cnt = dev->rdev.port_info.nports;
dev->ibdev.dma_device = &(dev->rdev.rnic_info.pdev->dev);
- dev->ibdev.class_dev.dev = &(dev->rdev.rnic_info.pdev->dev);
dev->ibdev.query_device = iwch_query_device;
dev->ibdev.query_port = iwch_query_port;
dev->ibdev.modify_port = iwch_modify_port;
diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h
index 82ded44c6cee..10fb8fbafa0c 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -106,6 +106,7 @@ struct ehca_shca {
struct ehca_mr *maxmr;
struct ehca_pd *pd;
struct h_galpas galpas;
+ struct mutex modify_mutex;
};
struct ehca_pd {
diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c
index 30eb45df9f0b..32b55a4f0e5b 100644
--- a/drivers/infiniband/hw/ehca/ehca_hca.c
+++ b/drivers/infiniband/hw/ehca/ehca_hca.c
@@ -147,6 +147,7 @@ int ehca_query_port(struct ib_device *ibdev,
break;
}
+ props->port_cap_flags = rblock->capability_mask;
props->gid_tbl_len = rblock->gid_tbl_len;
props->max_msg_sz = rblock->max_msg_sz;
props->bad_pkey_cntr = rblock->bad_pkey_cntr;
@@ -236,10 +237,60 @@ query_gid1:
return ret;
}
+const u32 allowed_port_caps = (
+ IB_PORT_SM | IB_PORT_LED_INFO_SUP | IB_PORT_CM_SUP |
+ IB_PORT_SNMP_TUNNEL_SUP | IB_PORT_DEVICE_MGMT_SUP |
+ IB_PORT_VENDOR_CLASS_SUP);
+
int ehca_modify_port(struct ib_device *ibdev,
u8 port, int port_modify_mask,
struct ib_port_modify *props)
{
- /* Not implemented yet */
- return -EFAULT;
+ int ret = 0;
+ struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device);
+ struct hipz_query_port *rblock;
+ u32 cap;
+ u64 hret;
+
+ if ((props->set_port_cap_mask | props->clr_port_cap_mask)
+ & ~allowed_port_caps) {
+ ehca_err(&shca->ib_device, "Non-changeable bits set in masks "
+ "set=%x clr=%x allowed=%x", props->set_port_cap_mask,
+ props->clr_port_cap_mask, allowed_port_caps);
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&shca->modify_mutex))
+ return -ERESTARTSYS;
+
+ rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
+ if (!rblock) {
+ ehca_err(&shca->ib_device, "Can't allocate rblock memory.");
+ ret = -ENOMEM;
+ goto modify_port1;
+ }
+
+ if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) {
+ ehca_err(&shca->ib_device, "Can't query port properties");
+ ret = -EINVAL;
+ goto modify_port2;
+ }
+
+ cap = (rblock->capability_mask | props->set_port_cap_mask)
+ & ~props->clr_port_cap_mask;
+
+ hret = hipz_h_modify_port(shca->ipz_hca_handle, port,
+ cap, props->init_type, port_modify_mask);
+ if (hret != H_SUCCESS) {
+ ehca_err(&shca->ib_device, "Modify port failed hret=%lx", hret);
+ ret = -EINVAL;
+ }
+
+modify_port2:
+ ehca_free_fw_ctrlblock(rblock);
+
+modify_port1:
+ mutex_unlock(&shca->modify_mutex);
+
+ return ret;
}
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index cee66b79b30d..4700085ba834 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -587,6 +587,7 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev,
ehca_gen_err("Cannot allocate shca memory.");
return -ENOMEM;
}
+ mutex_init(&shca->modify_mutex);
shca->ibmebus_dev = dev;
shca->ipz_hca_handle.handle = *handle;
diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c
index 3fb46e67df87..b564fcd3b282 100644
--- a/drivers/infiniband/hw/ehca/hcp_if.c
+++ b/drivers/infiniband/hw/ehca/hcp_if.c
@@ -70,6 +70,10 @@
#define H_ALL_RES_QP_SQUEUE_SIZE_PAGES EHCA_BMASK_IBM(0, 31)
#define H_ALL_RES_QP_RQUEUE_SIZE_PAGES EHCA_BMASK_IBM(32, 63)
+#define H_MP_INIT_TYPE EHCA_BMASK_IBM(44, 47)
+#define H_MP_SHUTDOWN EHCA_BMASK_IBM(48, 48)
+#define H_MP_RESET_QKEY_CTR EHCA_BMASK_IBM(49, 49)
+
/* direct access qp controls */
#define DAQP_CTRL_ENABLE 0x01
#define DAQP_CTRL_SEND_COMP 0x20
@@ -364,6 +368,26 @@ u64 hipz_h_query_port(const struct ipz_adapter_handle adapter_handle,
return ret;
}
+u64 hipz_h_modify_port(const struct ipz_adapter_handle adapter_handle,
+ const u8 port_id, const u32 port_cap,
+ const u8 init_type, const int modify_mask)
+{
+ u64 port_attributes = port_cap;
+
+ if (modify_mask & IB_PORT_SHUTDOWN)
+ port_attributes |= EHCA_BMASK_SET(H_MP_SHUTDOWN, 1);
+ if (modify_mask & IB_PORT_INIT_TYPE)
+ port_attributes |= EHCA_BMASK_SET(H_MP_INIT_TYPE, init_type);
+ if (modify_mask & IB_PORT_RESET_QKEY_CNTR)
+ port_attributes |= EHCA_BMASK_SET(H_MP_RESET_QKEY_CTR, 1);
+
+ return ehca_plpar_hcall_norets(H_MODIFY_PORT,
+ adapter_handle.handle, /* r4 */
+ port_id, /* r5 */
+ port_attributes, /* r6 */
+ 0, 0, 0, 0);
+}
+
u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle,
struct hipz_query_hca *query_hca_rblock)
{
diff --git a/drivers/infiniband/hw/ehca/hcp_if.h b/drivers/infiniband/hw/ehca/hcp_if.h
index 587ebd470959..2869f7dd6196 100644
--- a/drivers/infiniband/hw/ehca/hcp_if.h
+++ b/drivers/infiniband/hw/ehca/hcp_if.h
@@ -85,6 +85,10 @@ u64 hipz_h_query_port(const struct ipz_adapter_handle adapter_handle,
const u8 port_id,
struct hipz_query_port *query_port_response_block);
+u64 hipz_h_modify_port(const struct ipz_adapter_handle adapter_handle,
+ const u8 port_id, const u32 port_cap,
+ const u8 init_type, const int modify_mask);
+
u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle,
struct hipz_query_hca *query_hca_rblock);
diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h
index 54139d398181..10c008f22ba6 100644
--- a/drivers/infiniband/hw/ipath/ipath_common.h
+++ b/drivers/infiniband/hw/ipath/ipath_common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 QLogic, Inc. All rights reserved.
+ * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
@@ -78,6 +78,8 @@
#define IPATH_IB_LINKINIT 3
#define IPATH_IB_LINKDOWN_SLEEP 4
#define IPATH_IB_LINKDOWN_DISABLE 5
+#define IPATH_IB_LINK_LOOPBACK 6 /* enable local loopback */
+#define IPATH_IB_LINK_EXTERNAL 7 /* normal, disable local loopback */
/*
* stats maintained by the driver. For now, at least, this is global
@@ -316,11 +318,17 @@ struct ipath_base_info {
/* 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 */
+ /* shared memory pages for subports if port is shared */
__u64 spi_subport_uregbase;
__u64 spi_subport_rcvegrbuf;
__u64 spi_subport_rcvhdr_base;
+ /* shared memory page for hardware port if it is shared */
+ __u64 spi_port_uregbase;
+ __u64 spi_port_rcvegrbuf;
+ __u64 spi_port_rcvhdr_base;
+ __u64 spi_port_rcvhdr_tailaddr;
+
} __attribute__ ((aligned(8)));
@@ -344,7 +352,7 @@ struct ipath_base_info {
* may not be implemented; the user code must deal with this if it
* cares, or it must abort after initialization reports the difference.
*/
-#define IPATH_USER_SWMINOR 3
+#define IPATH_USER_SWMINOR 5
#define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR)
@@ -418,11 +426,14 @@ struct ipath_user_info {
#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_SLAVE_INFO 22 /* return info on slave processes (for old user code) */
#define IPATH_CMD_ASSIGN_PORT 23 /* allocate HCA and port */
#define IPATH_CMD_USER_INIT 24 /* set up userspace */
+#define IPATH_CMD_UNUSED_1 25
+#define IPATH_CMD_UNUSED_2 26
+#define IPATH_CMD_PIOAVAILUPD 27 /* force an update of PIOAvail reg */
-#define IPATH_CMD_MAX 24
+#define IPATH_CMD_MAX 27
struct ipath_port_info {
__u32 num_active; /* number of active units */
@@ -430,7 +441,7 @@ struct ipath_port_info {
__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 */
+ __u16 num_subports; /* number of subports opened on port */
};
struct ipath_tid_info {
diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c
index 87462e0cb4d2..ea78e6dddc90 100644
--- a/drivers/infiniband/hw/ipath/ipath_cq.c
+++ b/drivers/infiniband/hw/ipath/ipath_cq.c
@@ -76,7 +76,20 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
}
return;
}
- wc->queue[head] = *entry;
+ wc->queue[head].wr_id = entry->wr_id;
+ wc->queue[head].status = entry->status;
+ wc->queue[head].opcode = entry->opcode;
+ wc->queue[head].vendor_err = entry->vendor_err;
+ wc->queue[head].byte_len = entry->byte_len;
+ wc->queue[head].imm_data = (__u32 __force)entry->imm_data;
+ wc->queue[head].qp_num = entry->qp->qp_num;
+ wc->queue[head].src_qp = entry->src_qp;
+ wc->queue[head].wc_flags = entry->wc_flags;
+ wc->queue[head].pkey_index = entry->pkey_index;
+ wc->queue[head].slid = entry->slid;
+ wc->queue[head].sl = entry->sl;
+ wc->queue[head].dlid_path_bits = entry->dlid_path_bits;
+ wc->queue[head].port_num = entry->port_num;
wc->head = next;
if (cq->notify == IB_CQ_NEXT_COMP ||
@@ -122,9 +135,30 @@ int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
if (tail > (u32) cq->ibcq.cqe)
tail = (u32) cq->ibcq.cqe;
for (npolled = 0; npolled < num_entries; ++npolled, ++entry) {
+ struct ipath_qp *qp;
+
if (tail == wc->head)
break;
- *entry = wc->queue[tail];
+
+ qp = ipath_lookup_qpn(&to_idev(cq->ibcq.device)->qp_table,
+ wc->queue[tail].qp_num);
+ entry->qp = &qp->ibqp;
+ if (atomic_dec_and_test(&qp->refcount))
+ wake_up(&qp->wait);
+
+ entry->wr_id = wc->queue[tail].wr_id;
+ entry->status = wc->queue[tail].status;
+ entry->opcode = wc->queue[tail].opcode;
+ entry->vendor_err = wc->queue[tail].vendor_err;
+ entry->byte_len = wc->queue[tail].byte_len;
+ entry->imm_data = wc->queue[tail].imm_data;
+ entry->src_qp = wc->queue[tail].src_qp;
+ entry->wc_flags = wc->queue[tail].wc_flags;
+ entry->pkey_index = wc->queue[tail].pkey_index;
+ entry->slid = wc->queue[tail].slid;
+ entry->sl = wc->queue[tail].sl;
+ entry->dlid_path_bits = wc->queue[tail].dlid_path_bits;
+ entry->port_num = wc->queue[tail].port_num;
if (tail >= cq->ibcq.cqe)
tail = 0;
else
diff --git a/drivers/infiniband/hw/ipath/ipath_debug.h b/drivers/infiniband/hw/ipath/ipath_debug.h
index df69f0d80b8b..42bfbdb0d3e6 100644
--- a/drivers/infiniband/hw/ipath/ipath_debug.h
+++ b/drivers/infiniband/hw/ipath/ipath_debug.h
@@ -57,6 +57,7 @@
#define __IPATH_PROCDBG 0x100
/* print mmap/nopage stuff, not using VDBG any more */
#define __IPATH_MMDBG 0x200
+#define __IPATH_ERRPKTDBG 0x400
#define __IPATH_USER_SEND 0x1000 /* use user mode send */
#define __IPATH_KERNEL_SEND 0x2000 /* use kernel mode send */
#define __IPATH_EPKTDBG 0x4000 /* print ethernet packet data */
diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c
index 0f13a2182cc7..63e8368b0e95 100644
--- a/drivers/infiniband/hw/ipath/ipath_diag.c
+++ b/drivers/infiniband/hw/ipath/ipath_diag.c
@@ -296,7 +296,7 @@ static int ipath_diag_open(struct inode *in, struct file *fp)
}
fp->private_data = dd;
- ipath_diag_inuse = 1;
+ ipath_diag_inuse = -2;
diag_set_link = 0;
ret = 0;
@@ -461,6 +461,8 @@ static ssize_t ipath_diag_read(struct file *fp, char __user *data,
else if ((count % 4) || (*off % 4))
/* address or length is not 32-bit aligned, hence invalid */
ret = -EINVAL;
+ else if (ipath_diag_inuse < 1 && (*off || count != 8))
+ ret = -EINVAL; /* prevent cat /dev/ipath_diag* */
else if ((count % 8) || (*off % 8))
/* address or length not 64-bit aligned; do 32-bit reads */
ret = ipath_read_umem32(dd, data, kreg_base + *off, count);
@@ -470,6 +472,8 @@ static ssize_t ipath_diag_read(struct file *fp, char __user *data,
if (ret >= 0) {
*off += count;
ret = count;
+ if (ipath_diag_inuse == -2)
+ ipath_diag_inuse++;
}
return ret;
@@ -489,6 +493,9 @@ static ssize_t ipath_diag_write(struct file *fp, const char __user *data,
else if ((count % 4) || (*off % 4))
/* address or length is not 32-bit aligned, hence invalid */
ret = -EINVAL;
+ else if ((ipath_diag_inuse == -1 && (*off || count != 8)) ||
+ ipath_diag_inuse == -2) /* read qw off 0, write qw off 0 */
+ ret = -EINVAL; /* before any other write allowed */
else if ((count % 8) || (*off % 8))
/* address or length not 64-bit aligned; do 32-bit writes */
ret = ipath_write_umem32(dd, kreg_base + *off, data, count);
@@ -498,6 +505,8 @@ static ssize_t ipath_diag_write(struct file *fp, const char __user *data,
if (ret >= 0) {
*off += count;
ret = count;
+ if (ipath_diag_inuse == -1)
+ ipath_diag_inuse = 1; /* all read/write OK now */
}
return ret;
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index ae7f21a0cdc0..e3a223209710 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -390,15 +390,23 @@ static int __devinit ipath_init_one(struct pci_dev *pdev,
/* setup the chip-specific functions, as early as possible. */
switch (ent->device) {
-#ifdef CONFIG_HT_IRQ
case PCI_DEVICE_ID_INFINIPATH_HT:
+#ifdef CONFIG_HT_IRQ
ipath_init_iba6110_funcs(dd);
break;
+#else
+ ipath_dev_err(dd, "QLogic HT device 0x%x cannot work if "
+ "CONFIG_HT_IRQ is not enabled\n", ent->device);
+ return -ENODEV;
#endif
-#ifdef CONFIG_PCI_MSI
case PCI_DEVICE_ID_INFINIPATH_PE800:
+#ifdef CONFIG_PCI_MSI
ipath_init_iba6120_funcs(dd);
break;
+#else
+ ipath_dev_err(dd, "QLogic PCIE device 0x%x cannot work if "
+ "CONFIG_PCI_MSI is not enabled\n", ent->device);
+ return -ENODEV;
#endif
default:
ipath_dev_err(dd, "Found unknown QLogic deviceid 0x%x, "
@@ -486,7 +494,7 @@ static int __devinit ipath_init_one(struct pci_dev *pdev,
ret = ipath_init_chip(dd, 0); /* do the chip-specific init */
if (ret)
- goto bail_iounmap;
+ goto bail_irqsetup;
ret = ipath_enable_wc(dd);
@@ -505,6 +513,9 @@ static int __devinit ipath_init_one(struct pci_dev *pdev,
goto bail;
+bail_irqsetup:
+ if (pdev->irq) free_irq(pdev->irq, dd);
+
bail_iounmap:
iounmap((volatile void __iomem *) dd->ipath_kregbase);
@@ -525,8 +536,6 @@ 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;
@@ -594,8 +603,9 @@ static void __devexit cleanup_device(struct ipath_devdata *dd)
ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n",
dd->ipath_pageshadow);
- vfree(dd->ipath_pageshadow);
+ tmpp = dd->ipath_pageshadow;
dd->ipath_pageshadow = NULL;
+ vfree(tmpp);
}
/*
@@ -622,6 +632,12 @@ static void __devexit ipath_remove_one(struct pci_dev *pdev)
ipath_cdbg(VERBOSE, "removing, pdev=%p, dd=%p\n", pdev, dd);
+ /*
+ * disable the IB link early, to be sure no new packets arrive, which
+ * complicates the shutdown process
+ */
+ ipath_shutdown_device(dd);
+
if (dd->verbs_dev)
ipath_unregister_ib_device(dd->verbs_dev);
@@ -754,9 +770,42 @@ static int ipath_wait_linkstate(struct ipath_devdata *dd, u32 state,
return (dd->ipath_flags & state) ? 0 : -ETIMEDOUT;
}
-void ipath_decode_err(char *buf, size_t blen, ipath_err_t err)
+/*
+ * Decode the error status into strings, deciding whether to always
+ * print * it or not depending on "normal packet errors" vs everything
+ * else. Return 1 if "real" errors, otherwise 0 if only packet
+ * errors, so caller can decide what to print with the string.
+ */
+int ipath_decode_err(char *buf, size_t blen, ipath_err_t err)
{
+ int iserr = 1;
*buf = '\0';
+ if (err & INFINIPATH_E_PKTERRS) {
+ if (!(err & ~INFINIPATH_E_PKTERRS))
+ iserr = 0; // if only packet errors.
+ if (ipath_debug & __IPATH_ERRPKTDBG) {
+ if (err & INFINIPATH_E_REBP)
+ strlcat(buf, "EBP ", blen);
+ if (err & INFINIPATH_E_RVCRC)
+ strlcat(buf, "VCRC ", blen);
+ if (err & INFINIPATH_E_RICRC) {
+ strlcat(buf, "CRC ", blen);
+ // clear for check below, so only once
+ err &= INFINIPATH_E_RICRC;
+ }
+ if (err & INFINIPATH_E_RSHORTPKTLEN)
+ strlcat(buf, "rshortpktlen ", blen);
+ if (err & INFINIPATH_E_SDROPPEDDATAPKT)
+ strlcat(buf, "sdroppeddatapkt ", blen);
+ if (err & INFINIPATH_E_SPKTLEN)
+ strlcat(buf, "spktlen ", blen);
+ }
+ if ((err & INFINIPATH_E_RICRC) &&
+ !(err&(INFINIPATH_E_RVCRC|INFINIPATH_E_REBP)))
+ strlcat(buf, "CRC ", blen);
+ if (!iserr)
+ goto done;
+ }
if (err & INFINIPATH_E_RHDRLEN)
strlcat(buf, "rhdrlen ", blen);
if (err & INFINIPATH_E_RBADTID)
@@ -767,12 +816,12 @@ void ipath_decode_err(char *buf, size_t blen, ipath_err_t err)
strlcat(buf, "rhdr ", blen);
if (err & INFINIPATH_E_RLONGPKTLEN)
strlcat(buf, "rlongpktlen ", blen);
- if (err & INFINIPATH_E_RSHORTPKTLEN)
- strlcat(buf, "rshortpktlen ", blen);
if (err & INFINIPATH_E_RMAXPKTLEN)
strlcat(buf, "rmaxpktlen ", blen);
if (err & INFINIPATH_E_RMINPKTLEN)
strlcat(buf, "rminpktlen ", blen);
+ if (err & INFINIPATH_E_SMINPKTLEN)
+ strlcat(buf, "sminpktlen ", blen);
if (err & INFINIPATH_E_RFORMATERR)
strlcat(buf, "rformaterr ", blen);
if (err & INFINIPATH_E_RUNSUPVL)
@@ -781,32 +830,20 @@ void ipath_decode_err(char *buf, size_t blen, ipath_err_t err)
strlcat(buf, "runexpchar ", blen);
if (err & INFINIPATH_E_RIBFLOW)
strlcat(buf, "ribflow ", blen);
- if (err & INFINIPATH_E_REBP)
- strlcat(buf, "EBP ", blen);
if (err & INFINIPATH_E_SUNDERRUN)
strlcat(buf, "sunderrun ", blen);
if (err & INFINIPATH_E_SPIOARMLAUNCH)
strlcat(buf, "spioarmlaunch ", blen);
if (err & INFINIPATH_E_SUNEXPERRPKTNUM)
strlcat(buf, "sunexperrpktnum ", blen);
- if (err & INFINIPATH_E_SDROPPEDDATAPKT)
- strlcat(buf, "sdroppeddatapkt ", blen);
if (err & INFINIPATH_E_SDROPPEDSMPPKT)
strlcat(buf, "sdroppedsmppkt ", blen);
if (err & INFINIPATH_E_SMAXPKTLEN)
strlcat(buf, "smaxpktlen ", blen);
- if (err & INFINIPATH_E_SMINPKTLEN)
- strlcat(buf, "sminpktlen ", blen);
if (err & INFINIPATH_E_SUNSUPVL)
strlcat(buf, "sunsupVL ", blen);
- if (err & INFINIPATH_E_SPKTLEN)
- strlcat(buf, "spktlen ", blen);
if (err & INFINIPATH_E_INVALIDADDR)
strlcat(buf, "invalidaddr ", blen);
- if (err & INFINIPATH_E_RICRC)
- strlcat(buf, "CRC ", blen);
- if (err & INFINIPATH_E_RVCRC)
- strlcat(buf, "VCRC ", blen);
if (err & INFINIPATH_E_RRCVEGRFULL)
strlcat(buf, "rcvegrfull ", blen);
if (err & INFINIPATH_E_RRCVHDRFULL)
@@ -819,6 +856,8 @@ void ipath_decode_err(char *buf, size_t blen, ipath_err_t err)
strlcat(buf, "hardware ", blen);
if (err & INFINIPATH_E_RESET)
strlcat(buf, "reset ", blen);
+done:
+ return iserr;
}
/**
@@ -1662,6 +1701,22 @@ int ipath_set_linkstate(struct ipath_devdata *dd, u8 newstate)
lstate = IPATH_LINKACTIVE;
break;
+ case IPATH_IB_LINK_LOOPBACK:
+ dev_info(&dd->pcidev->dev, "Enabling IB local loopback\n");
+ dd->ipath_ibcctrl |= INFINIPATH_IBCC_LOOPBACK;
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+ dd->ipath_ibcctrl);
+ ret = 0;
+ goto bail; // no state change to wait for
+
+ case IPATH_IB_LINK_EXTERNAL:
+ dev_info(&dd->pcidev->dev, "Disabling IB local loopback (normal)\n");
+ dd->ipath_ibcctrl &= ~INFINIPATH_IBCC_LOOPBACK;
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
+ dd->ipath_ibcctrl);
+ ret = 0;
+ goto bail; // no state change to wait for
+
default:
ipath_dbg("Invalid linkstate 0x%x requested\n", newstate);
ret = -EINVAL;
@@ -1765,29 +1820,6 @@ int ipath_set_lid(struct ipath_devdata *dd, u32 arg, u8 lmc)
return 0;
}
-/**
- * ipath_read_kreg64_port - read a device's per-port 64-bit kernel register
- * @dd: the infinipath device
- * @regno: the register number to read
- * @port: the port containing the register
- *
- * Registers that vary with the chip implementation constants (port)
- * use this routine.
- */
-u64 ipath_read_kreg64_port(const struct ipath_devdata *dd, ipath_kreg regno,
- unsigned port)
-{
- u16 where;
-
- if (port < dd->ipath_portcnt &&
- (regno == dd->ipath_kregs->kr_rcvhdraddr ||
- regno == dd->ipath_kregs->kr_rcvhdrtailaddr))
- where = regno + port;
- else
- where = -1;
-
- return ipath_read_kreg64(dd, where);
-}
/**
* ipath_write_kreg_port - write a device's per-port 64-bit kernel register
@@ -1973,7 +2005,8 @@ static int __init infinipath_init(void)
{
int ret;
- ipath_dbg(KERN_INFO DRIVER_LOAD_MSG "%s", ib_ipath_version);
+ if (ipath_debug & __IPATH_DBG)
+ printk(KERN_INFO DRIVER_LOAD_MSG "%s", ib_ipath_version);
/*
* These must be called before the driver is registered with
diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c
index a4019a6b7560..030185f90ee2 100644
--- a/drivers/infiniband/hw/ipath/ipath_eeprom.c
+++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c
@@ -626,6 +626,10 @@ void ipath_get_eeprom_info(struct ipath_devdata *dd)
} else
memcpy(dd->ipath_serial, ifp->if_serial,
sizeof ifp->if_serial);
+ if (!strstr(ifp->if_comment, "Tested successfully"))
+ ipath_dev_err(dd, "Board SN %s did not pass functional "
+ "test: %s\n", dd->ipath_serial,
+ ifp->if_comment);
ipath_cdbg(VERBOSE, "Initted GUID to %llx from eeprom\n",
(unsigned long long) be64_to_cpu(dd->ipath_guid));
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 5d64ff875297..1272aaf2a785 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 QLogic, Inc. All rights reserved.
+ * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
@@ -41,12 +41,6 @@
#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,
@@ -63,6 +57,24 @@ static const struct file_operations ipath_file_ops = {
.mmap = ipath_mmap
};
+/*
+ * Convert kernel virtual addresses to physical addresses so they don't
+ * potentially conflict with the chip addresses used as mmap offsets.
+ * It doesn't really matter what mmap offset we use as long as we can
+ * interpret it correctly.
+ */
+static u64 cvt_kvaddr(void *p)
+{
+ struct page *page;
+ u64 paddr = 0;
+
+ page = vmalloc_to_page(p);
+ if (page)
+ paddr = page_to_pfn(page) << PAGE_SHIFT;
+
+ return paddr;
+}
+
static int ipath_get_base_info(struct file *fp,
void __user *ubase, size_t ubase_size)
{
@@ -87,7 +99,7 @@ static int ipath_get_base_info(struct file *fp,
sz = sizeof(*kinfo);
/* If port sharing is not requested, allow the old size structure */
if (!shared)
- sz -= 3 * sizeof(u64);
+ sz -= 7 * sizeof(u64);
if (ubase_size < sz) {
ipath_cdbg(PROC,
"Base size %zu, need %zu (version mismatch?)\n",
@@ -165,24 +177,41 @@ static int ipath_get_base_info(struct file *fp,
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;
+ }
+ if (shared) {
+ kinfo->spi_port_uregbase = (u64) dd->ipath_uregbase +
+ dd->ipath_palign * pd->port_port;
+ kinfo->spi_port_rcvegrbuf = kinfo->spi_rcv_egrbufs;
+ kinfo->spi_port_rcvhdr_base = kinfo->spi_rcvhdr_base;
+ kinfo->spi_port_rcvhdr_tailaddr = kinfo->spi_rcvhdr_tailaddr;
- 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_uregbase = cvt_kvaddr(pd->subport_uregbase +
+ PAGE_SIZE * subport_fp(fp));
+
+ kinfo->spi_rcvhdr_base = cvt_kvaddr(pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * subport_fp(fp));
+ kinfo->spi_rcvhdr_tailaddr = 0;
+ kinfo->spi_rcv_egrbufs = cvt_kvaddr(pd->subport_rcvegrbuf +
+ pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size *
+ subport_fp(fp));
+
+ kinfo->spi_subport_uregbase =
+ cvt_kvaddr(pd->subport_uregbase);
+ kinfo->spi_subport_rcvegrbuf =
+ cvt_kvaddr(pd->subport_rcvegrbuf);
+ kinfo->spi_subport_rcvhdr_base =
+ cvt_kvaddr(pd->subport_rcvhdr_base);
+ 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);
}
kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) /
@@ -199,20 +228,10 @@ static int ipath_get_base_info(struct file *fp,
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)))
+ sz = (ubase_size < sizeof(*kinfo)) ? ubase_size : sizeof(*kinfo);
+ if (copy_to_user(ubase, kinfo, sz))
ret = -EFAULT;
bail:
@@ -1132,67 +1151,55 @@ static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr,
struct ipath_devdata *dd;
void *addr;
size_t size;
- int ret;
+ int ret = 0;
/* If the port is not shared, all addresses should be physical */
- if (!pd->port_subport_cnt) {
- ret = -EINVAL;
+ if (!pd->port_subport_cnt)
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.
+ * Each process has all the subport uregbase, rcvhdrq, and
+ * rcvegrbufs mmapped - as an array for all the processes,
+ * and also separately for this process.
*/
- 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;
+ if (pgaddr == cvt_kvaddr(pd->subport_uregbase)) {
+ addr = pd->subport_uregbase;
+ size = PAGE_SIZE * pd->port_subport_cnt;
+ } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base)) {
+ addr = pd->subport_rcvhdr_base;
+ size = pd->port_rcvhdrq_size * pd->port_subport_cnt;
+ } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf)) {
+ addr = pd->subport_rcvegrbuf;
+ size *= pd->port_subport_cnt;
+ } else if (pgaddr == cvt_kvaddr(pd->subport_uregbase +
+ PAGE_SIZE * subport)) {
+ addr = pd->subport_uregbase + PAGE_SIZE * subport;
+ size = PAGE_SIZE;
+ } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * subport)) {
+ addr = pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * subport;
+ size = pd->port_rcvhdrq_size;
+ } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf +
+ size * subport)) {
+ addr = pd->subport_rcvegrbuf + size * subport;
+ /* 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;
@@ -1205,7 +1212,7 @@ static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr,
vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT;
vma->vm_ops = &ipath_file_vm_ops;
vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
- ret = 0;
+ ret = 1;
bail:
return ret;
@@ -1265,19 +1272,20 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
* 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));
+ ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp));
+ if (ret) {
+ if (ret > 0)
+ ret = 0;
goto bail;
}
+ ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
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 +
@@ -1286,7 +1294,6 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
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;
}
@@ -1300,9 +1307,6 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
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)
@@ -1400,32 +1404,41 @@ static int init_subports(struct ipath_devdata *dd,
const struct ipath_user_info *uinfo)
{
int ret = 0;
- unsigned num_slaves;
+ unsigned num_subports;
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) {
+
+ /* Old user binaries don't know about new subport implementation */
+ if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) {
+ dev_info(&dd->pcidev->dev,
+ "Mismatched user minor version (%d) and driver "
+ "minor version (%d) while port sharing. Ensure "
+ "that driver and library are from the same "
+ "release.\n",
+ (int) (uinfo->spu_userversion & 0xffff),
+ IPATH_USER_SWMINOR);
+ goto bail;
+ }
+ if (uinfo->spu_subport_cnt > INFINIPATH_MAX_SUBPORT) {
ret = -EINVAL;
goto bail;
}
- num_slaves = uinfo->spu_subport_cnt - 1;
- pd->subport_uregbase = vmalloc(PAGE_SIZE * num_slaves);
+ num_subports = uinfo->spu_subport_cnt;
+ pd->subport_uregbase = vmalloc(PAGE_SIZE * num_subports);
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;
+ sizeof(u32), PAGE_SIZE) * num_subports;
pd->subport_rcvhdr_base = vmalloc(size);
if (!pd->subport_rcvhdr_base) {
ret = -ENOMEM;
@@ -1434,7 +1447,7 @@ static int init_subports(struct ipath_devdata *dd,
pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks *
pd->port_rcvegrbuf_size *
- num_slaves);
+ num_subports);
if (!pd->subport_rcvegrbuf) {
ret = -ENOMEM;
goto bail_rhdr;
@@ -1443,6 +1456,12 @@ static int init_subports(struct ipath_devdata *dd,
pd->port_subport_cnt = uinfo->spu_subport_cnt;
pd->port_subport_id = uinfo->spu_subport_id;
pd->active_slaves = 1;
+ set_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag);
+ memset(pd->subport_uregbase, 0, PAGE_SIZE * num_subports);
+ memset(pd->subport_rcvhdr_base, 0, size);
+ memset(pd->subport_rcvegrbuf, 0, pd->port_rcvegrbuf_chunks *
+ pd->port_rcvegrbuf_size *
+ num_subports);
goto bail;
bail_rhdr:
@@ -1573,18 +1592,19 @@ static int find_best_unit(struct file *fp,
*/
if (!cpus_empty(current->cpus_allowed) &&
!cpus_full(current->cpus_allowed)) {
- int ncpus = num_online_cpus(), curcpu = -1;
+ int ncpus = num_online_cpus(), curcpu = -1, nset = 0;
for (i = 0; i < ncpus; i++)
if (cpu_isset(i, current->cpus_allowed)) {
ipath_cdbg(PROC, "%s[%u] affinity set for "
- "cpu %d\n", current->comm,
- current->pid, i);
+ "cpu %d/%d\n", current->comm,
+ current->pid, i, ncpus);
curcpu = i;
+ nset++;
}
- if (curcpu != -1) {
+ if (curcpu != -1 && nset != ncpus) {
if (npresent) {
prefunit = curcpu / (ncpus / npresent);
- ipath_dbg("%s[%u] %d chips, %d cpus, "
+ ipath_cdbg(PROC,"%s[%u] %d chips, %d cpus, "
"%d cpus/chip, select unit %d\n",
current->comm, current->pid,
npresent, ncpus, ncpus / npresent,
@@ -1764,11 +1784,17 @@ static int ipath_do_user_init(struct file *fp,
const struct ipath_user_info *uinfo)
{
int ret;
- struct ipath_portdata *pd;
+ struct ipath_portdata *pd = port_fp(fp);
struct ipath_devdata *dd;
u32 head32;
- pd = port_fp(fp);
+ /* Subports don't need to initialize anything since master did it. */
+ if (subport_fp(fp)) {
+ ret = wait_event_interruptible(pd->port_wait,
+ !test_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag));
+ goto done;
+ }
+
dd = pd->port_dd;
if (uinfo->spu_rcvhdrsize) {
@@ -1826,6 +1852,11 @@ static int ipath_do_user_init(struct file *fp,
dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
+ /* Notify any waiting slaves */
+ if (pd->port_subport_cnt) {
+ clear_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag);
+ wake_up(&pd->port_wait);
+ }
done:
return ret;
}
@@ -2017,6 +2048,17 @@ static int ipath_get_slave_info(struct ipath_portdata *pd,
return ret;
}
+static int ipath_force_pio_avail_update(struct ipath_devdata *dd)
+{
+ u64 reg = dd->ipath_sendctrl;
+
+ clear_bit(IPATH_S_PIOBUFAVAILUPD, &reg);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, reg);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);
+
+ return 0;
+}
+
static ssize_t ipath_write(struct file *fp, const char __user *data,
size_t count, loff_t *off)
{
@@ -2071,27 +2113,35 @@ 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:
+ 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;
+ case IPATH_CMD_PIOAVAILUPD: // force an update of PIOAvail reg
+ copy = 0;
+ src = NULL;
+ dest = NULL;
+ break;
default:
ret = -EINVAL;
goto bail;
}
- if ((count - consumed) < copy) {
- ret = -EINVAL;
- goto bail;
- }
+ if (copy) {
+ if ((count - consumed) < copy) {
+ ret = -EINVAL;
+ goto bail;
+ }
- if (copy_from_user(dest, src, copy)) {
- ret = -EFAULT;
- goto bail;
+ if (copy_from_user(dest, src, copy)) {
+ ret = -EFAULT;
+ goto bail;
+ }
+
+ consumed += copy;
}
- consumed += copy;
pd = port_fp(fp);
if (!pd && cmd.type != __IPATH_CMD_USER_INIT &&
cmd.type != IPATH_CMD_ASSIGN_PORT) {
@@ -2137,11 +2187,14 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
case IPATH_CMD_SET_PART_KEY:
ret = ipath_set_part_key(pd, cmd.cmd.part_key);
break;
- case IPATH_CMD_SLAVE_INFO:
+ case __IPATH_CMD_SLAVE_INFO:
ret = ipath_get_slave_info(pd,
(void __user *) (unsigned long)
cmd.cmd.slave_mask_addr);
break;
+ case IPATH_CMD_PIOAVAILUPD:
+ ret = ipath_force_pio_avail_update(pd->port_dd);
+ break;
}
if (ret >= 0)
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c
index 993482545021..4171198fc202 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -43,6 +43,9 @@
#include "ipath_kernel.h"
#include "ipath_registers.h"
+static void ipath_setup_ht_setextled(struct ipath_devdata *, u64, u64);
+
+
/*
* This lists the InfiniPath registers, in the actual chip layout.
* This structure should never be directly accessed.
@@ -208,8 +211,8 @@ static const struct ipath_kregs ipath_ht_kregs = {
.kr_serdesstatus = IPATH_KREG_OFFSET(SerdesStatus),
.kr_xgxsconfig = IPATH_KREG_OFFSET(XGXSConfig),
/*
- * These should not be used directly via ipath_read_kreg64(),
- * use them with ipath_read_kreg64_port(),
+ * These should not be used directly via ipath_write_kreg64(),
+ * use them with ipath_write_kreg64_port(),
*/
.kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0),
.kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0)
@@ -284,6 +287,14 @@ static const struct ipath_cregs ipath_ht_cregs = {
#define INFINIPATH_EXTS_MEMBIST_ENDTEST 0x0000000000004000
#define INFINIPATH_EXTS_MEMBIST_CORRECT 0x0000000000008000
+
+/* TID entries (memory), HT-only */
+#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL /* 40 bits valid */
+#define INFINIPATH_RT_VALID 0x8000000000000000ULL
+#define INFINIPATH_RT_ADDR_SHIFT 0
+#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFFULL
+#define INFINIPATH_RT_BUFSIZE_SHIFT 48
+
/*
* masks and bits that are different in different chips, or present only
* in one
@@ -402,6 +413,14 @@ static const struct ipath_hwerror_msgs ipath_6110_hwerror_msgs[] = {
INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"),
};
+#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)
+#define RXE_EAGER_PARITY (INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID \
+ << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)
+
+static int ipath_ht_txe_recover(struct ipath_devdata *);
+
/**
* ipath_ht_handle_hwerrors - display hardware errors.
* @dd: the infinipath device
@@ -450,13 +469,12 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
/*
* make sure we get this much out, unless told to be quiet,
+ * it's a parity error we may recover from,
* or it's occurred within the last 5 seconds
*/
- if ((hwerrs & ~(dd->ipath_lasthwerror |
- ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
- INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
- << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) ||
- (ipath_debug & __IPATH_VERBDBG))
+ if ((hwerrs & ~(dd->ipath_lasthwerror | TXE_PIO_PARITY |
+ RXE_EAGER_PARITY)) ||
+ (ipath_debug & __IPATH_VERBDBG))
dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
"(cleared)\n", (unsigned long long) hwerrs);
dd->ipath_lasthwerror |= hwerrs;
@@ -467,7 +485,7 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
(hwerrs & ~dd->ipath_hwe_bitsextant));
ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
- if (ctrl & INFINIPATH_C_FREEZEMODE) {
+ if ((ctrl & INFINIPATH_C_FREEZEMODE) && !ipath_diag_inuse) {
/*
* parity errors in send memory are recoverable,
* just cancel the send (if indicated in * sendbuffererror),
@@ -476,50 +494,14 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
* 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 complaint once, in case it's stuck
- * or recurring, and we get here multiple
- * times.
- */
- if (dd->ipath_flags & IPATH_INITTED) {
- ipath_dev_err(dd, "Fatal Hardware Error (freeze "
- "mode), no longer usable, SN %.16s\n",
- dd->ipath_serial);
- isfatal = 1;
- }
- *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
- /* mark as having had error */
- *dd->ipath_statusp |= IPATH_STATUS_HWERROR;
- /*
- * mark as not usable, at a minimum until driver
- * is reloaded, probably until reboot, since no
- * other reset is possible.
- */
- dd->ipath_flags &= ~IPATH_INITTED;
- } else {
- ipath_dbg("Clearing freezemode on ignored hardware "
- "error\n");
+ if ((hwerrs & TXE_PIO_PARITY) && ipath_ht_txe_recover(dd))
+ hwerrs &= ~TXE_PIO_PARITY;
+ if (hwerrs & RXE_EAGER_PARITY)
+ ipath_dev_err(dd, "RXE parity, Eager TID error is not "
+ "recoverable\n");
+ if (!hwerrs) {
+ ipath_dbg("Clearing freezemode on ignored or "
+ "recovered hardware error\n");
ctrl &= ~INFINIPATH_C_FREEZEMODE;
ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
ctrl);
@@ -587,7 +569,39 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
dd->ipath_hwerrmask);
}
- ipath_dev_err(dd, "%s hardware error\n", msg);
+ if (hwerrs) {
+ /*
+ * if any set that we aren't ignoring; only
+ * make the complaint once, in case it's stuck
+ * or recurring, and we get here multiple
+ * times.
+ * force link down, so switch knows, and
+ * LEDs are turned off
+ */
+ if (dd->ipath_flags & IPATH_INITTED) {
+ ipath_set_linkstate(dd, IPATH_IB_LINKDOWN);
+ ipath_setup_ht_setextled(dd,
+ INFINIPATH_IBCS_L_STATE_DOWN,
+ INFINIPATH_IBCS_LT_STATE_DISABLED);
+ ipath_dev_err(dd, "Fatal Hardware Error (freeze "
+ "mode), no longer usable, SN %.16s\n",
+ dd->ipath_serial);
+ isfatal = 1;
+ }
+ *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY;
+ /* mark as having had error */
+ *dd->ipath_statusp |= IPATH_STATUS_HWERROR;
+ /*
+ * mark as not usable, at a minimum until driver
+ * is reloaded, probably until reboot, since no
+ * other reset is possible.
+ */
+ dd->ipath_flags &= ~IPATH_INITTED;
+ }
+ else
+ *msg = 0; /* recovered from all of them */
+ if (*msg)
+ ipath_dev_err(dd, "%s hardware error\n", msg);
if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg)
/*
* for status file; if no trailing brace is copied,
@@ -658,7 +672,8 @@ static int ipath_ht_boardname(struct ipath_devdata *dd, char *name,
if (n)
snprintf(name, namelen, "%s", n);
- if (dd->ipath_majrev != 3 || (dd->ipath_minrev < 2 || dd->ipath_minrev > 3)) {
+ if (dd->ipath_majrev != 3 || (dd->ipath_minrev < 2 ||
+ dd->ipath_minrev > 3)) {
/*
* This version of the driver only supports Rev 3.2 and 3.3
*/
@@ -1163,6 +1178,8 @@ static void ipath_ht_init_hwerrors(struct ipath_devdata *dd)
if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST))
ipath_dev_err(dd, "MemBIST did not complete!\n");
+ if (extsval & INFINIPATH_EXTS_MEMBIST_CORRECT)
+ ipath_dbg("MemBIST corrected\n");
ipath_check_htlink(dd);
@@ -1366,6 +1383,9 @@ static void ipath_ht_put_tid(struct ipath_devdata *dd,
u64 __iomem *tidptr, u32 type,
unsigned long pa)
{
+ if (!dd->ipath_kregbase)
+ return;
+
if (pa != dd->ipath_tidinvalid) {
if (unlikely((pa & ~INFINIPATH_RT_ADDR_MASK))) {
dev_info(&dd->pcidev->dev,
@@ -1382,10 +1402,10 @@ static void ipath_ht_put_tid(struct ipath_devdata *dd,
pa |= lenvalid | INFINIPATH_RT_VALID;
}
}
- if (dd->ipath_kregbase)
- writeq(pa, tidptr);
+ writeq(pa, tidptr);
}
+
/**
* ipath_ht_clear_tid - clear all TID entries for a port, expected and eager
* @dd: the infinipath device
@@ -1515,7 +1535,7 @@ static int ipath_ht_early_init(struct ipath_devdata *dd)
INFINIPATH_S_ABORT);
ipath_get_eeprom_info(dd);
- if(dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' &&
+ if (dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' &&
dd->ipath_serial[1] == '2' && dd->ipath_serial[2] == '8') {
/*
* Later production QHT7040 has same changes as QHT7140, so
@@ -1528,6 +1548,24 @@ static int ipath_ht_early_init(struct ipath_devdata *dd)
return 0;
}
+
+static int ipath_ht_txe_recover(struct ipath_devdata *dd)
+{
+ int cnt = ++ipath_stats.sps_txeparity;
+ if (cnt >= IPATH_MAX_PARITY_ATTEMPTS) {
+ if (cnt == IPATH_MAX_PARITY_ATTEMPTS)
+ ipath_dev_err(dd,
+ "Too many attempts to recover from "
+ "TXE parity, giving up\n");
+ return 0;
+ }
+ dev_info(&dd->pcidev->dev,
+ "Recovering from TXE PIO parity error\n");
+ ipath_disarm_senderrbufs(dd, 1);
+ return 1;
+}
+
+
/**
* ipath_init_ht_get_base_info - set chip-specific flags for user code
* @dd: the infinipath device
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c
index 05918e1e7c36..1b9c30857754 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6120.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c
@@ -43,6 +43,8 @@
#include "ipath_kernel.h"
#include "ipath_registers.h"
+static void ipath_setup_pe_setextled(struct ipath_devdata *, u64, u64);
+
/*
* This file contains all the chip-specific register information and
* access functions for the QLogic InfiniPath PCI-Express chip.
@@ -207,8 +209,8 @@ static const struct ipath_kregs ipath_pe_kregs = {
.kr_ibpllcfg = IPATH_KREG_OFFSET(IBPLLCfg),
/*
- * These should not be used directly via ipath_read_kreg64(),
- * use them with ipath_read_kreg64_port()
+ * These should not be used directly via ipath_write_kreg64(),
+ * use them with ipath_write_kreg64_port(),
*/
.kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0),
.kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0),
@@ -321,6 +323,12 @@ static const struct ipath_hwerror_msgs ipath_6120_hwerror_msgs[] = {
INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"),
};
+#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)
+
+static int ipath_pe_txe_recover(struct ipath_devdata *);
+
/**
* ipath_pe_handle_hwerrors - display hardware errors.
* @dd: the infinipath device
@@ -394,32 +402,21 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
* 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 & TXE_PIO_PARITY) && ipath_pe_txe_recover(dd))
+ hwerrs &= ~TXE_PIO_PARITY;
if (hwerrs) {
/*
* if any set that we aren't ignoring only make the
* complaint once, in case it's stuck or recurring,
* and we get here multiple times
+ * Force link down, so switch knows, and
+ * LEDs are turned off
*/
if (dd->ipath_flags & IPATH_INITTED) {
+ ipath_set_linkstate(dd, IPATH_IB_LINKDOWN);
+ ipath_setup_pe_setextled(dd,
+ INFINIPATH_IBCS_L_STATE_DOWN,
+ INFINIPATH_IBCS_LT_STATE_DISABLED);
ipath_dev_err(dd, "Fatal Hardware Error (freeze "
"mode), no longer usable, SN %.16s\n",
dd->ipath_serial);
@@ -493,7 +490,8 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
dd->ipath_hwerrmask);
}
- ipath_dev_err(dd, "%s hardware error\n", msg);
+ if (*msg)
+ ipath_dev_err(dd, "%s hardware error\n", msg);
if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) {
/*
* for /sys status file ; if no trailing } is copied, we'll
@@ -581,6 +579,8 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd)
if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST))
ipath_dev_err(dd, "MemBIST did not complete!\n");
+ if (extsval & INFINIPATH_EXTS_MEMBIST_FOUND)
+ ipath_dbg("MemBIST corrected\n");
val = ~0ULL; /* barring bugs, all hwerrors become interrupts, */
@@ -1330,6 +1330,35 @@ static void ipath_pe_free_irq(struct ipath_devdata *dd)
dd->ipath_irq = 0;
}
+/*
+ * On platforms using this chip, and not having ordered WC stores, we
+ * can get TXE parity errors due to speculative reads to the PIO buffers,
+ * and this, due to a chip bug can result in (many) false parity error
+ * reports. So it's a debug print on those, and an info print on systems
+ * where the speculative reads don't occur.
+ * Because we can get lots of false errors, we have no upper limit
+ * on recovery attempts on those platforms.
+ */
+static int ipath_pe_txe_recover(struct ipath_devdata *dd)
+{
+ if (ipath_unordered_wc())
+ ipath_dbg("Recovering from TXE PIO parity error\n");
+ else {
+ int cnt = ++ipath_stats.sps_txeparity;
+ if (cnt >= IPATH_MAX_PARITY_ATTEMPTS) {
+ if (cnt == IPATH_MAX_PARITY_ATTEMPTS)
+ ipath_dev_err(dd,
+ "Too many attempts to recover from "
+ "TXE parity, giving up\n");
+ return 0;
+ }
+ dev_info(&dd->pcidev->dev,
+ "Recovering from TXE PIO parity error\n");
+ }
+ ipath_disarm_senderrbufs(dd, 1);
+ return 1;
+}
+
/**
* ipath_init_iba6120_funcs - set up the chip-specific function pointers
* @dd: the infinipath device
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index d4f6b5239ef8..7045ba689494 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -216,6 +216,20 @@ static int bringup_link(struct ipath_devdata *dd)
return ret;
}
+static struct ipath_portdata *create_portdata0(struct ipath_devdata *dd)
+{
+ struct ipath_portdata *pd = NULL;
+
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ if (pd) {
+ pd->port_dd = dd;
+ pd->port_cnt = 1;
+ /* The port 0 pkey table is used by the layer interface. */
+ pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY;
+ }
+ return pd;
+}
+
static int init_chip_first(struct ipath_devdata *dd,
struct ipath_portdata **pdp)
{
@@ -271,20 +285,16 @@ static int init_chip_first(struct ipath_devdata *dd,
goto done;
}
- dd->ipath_pd[0] = kzalloc(sizeof(*pd), GFP_KERNEL);
+ pd = create_portdata0(dd);
- if (!dd->ipath_pd[0]) {
+ if (!pd) {
ipath_dev_err(dd, "Unable to allocate portdata for port "
"0, failing\n");
ret = -ENOMEM;
goto done;
}
- pd = dd->ipath_pd[0];
- pd->port_dd = dd;
- pd->port_port = 0;
- pd->port_cnt = 1;
- /* The port 0 pkey table is used by the layer interface. */
- pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY;
+ dd->ipath_pd[0] = pd;
+
dd->ipath_rcvtidcnt =
ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt);
dd->ipath_rcvtidbase =
@@ -590,6 +600,10 @@ static int init_housekeeping(struct ipath_devdata *dd,
goto done;
}
+
+ /* clear diagctrl register, in case diags were running and crashed */
+ ipath_write_kreg (dd, dd->ipath_kregs->kr_hwdiagctrl, 0);
+
/* clear the initial reset flag, in case first driver load */
ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear,
INFINIPATH_E_RESET);
@@ -668,6 +682,7 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
{
int ret = 0, i;
u32 val32, kpiobufs;
+ u32 piobufs, uports;
u64 val;
struct ipath_portdata *pd = NULL; /* keep gcc4 happy */
gfp_t gfp_flags = GFP_USER | __GFP_COMP;
@@ -702,16 +717,17 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
* the in memory DMA'ed copies of the registers. This has to
* be done early, before we calculate lastport, etc.
*/
- val = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
+ piobufs = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
/*
* calc number of pioavail registers, and save it; we have 2
* bits per buffer.
*/
- dd->ipath_pioavregs = ALIGN(val, sizeof(u64) * BITS_PER_BYTE / 2)
+ dd->ipath_pioavregs = ALIGN(piobufs, sizeof(u64) * BITS_PER_BYTE / 2)
/ (sizeof(u64) * BITS_PER_BYTE / 2);
+ uports = dd->ipath_cfgports ? dd->ipath_cfgports - 1 : 0;
if (ipath_kpiobufs == 0) {
/* not set by user (this is default) */
- if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) > 128)
+ if (piobufs >= (uports * IPATH_MIN_USER_PORT_BUFCNT) + 32)
kpiobufs = 32;
else
kpiobufs = 16;
@@ -719,31 +735,25 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
else
kpiobufs = ipath_kpiobufs;
- if (kpiobufs >
- (dd->ipath_piobcnt2k + dd->ipath_piobcnt4k -
- (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT))) {
- i = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k -
- (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT);
+ if (kpiobufs + (uports * IPATH_MIN_USER_PORT_BUFCNT) > piobufs) {
+ i = (int) piobufs -
+ (int) (uports * IPATH_MIN_USER_PORT_BUFCNT);
if (i < 0)
i = 0;
- dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs for "
- "kernel leaves too few for %d user ports "
+ dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs of "
+ "%d for kernel leaves too few for %d user ports "
"(%d each); using %u\n", kpiobufs,
- dd->ipath_cfgports - 1,
- IPATH_MIN_USER_PORT_BUFCNT, i);
+ piobufs, uports, IPATH_MIN_USER_PORT_BUFCNT, i);
/*
* shouldn't change ipath_kpiobufs, because could be
* different for different devices...
*/
kpiobufs = i;
}
- dd->ipath_lastport_piobuf =
- dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - kpiobufs;
- dd->ipath_pbufsport = dd->ipath_cfgports > 1
- ? dd->ipath_lastport_piobuf / (dd->ipath_cfgports - 1)
- : 0;
- val32 = dd->ipath_lastport_piobuf -
- (dd->ipath_pbufsport * (dd->ipath_cfgports - 1));
+ dd->ipath_lastport_piobuf = piobufs - kpiobufs;
+ dd->ipath_pbufsport =
+ uports ? dd->ipath_lastport_piobuf / uports : 0;
+ val32 = dd->ipath_lastport_piobuf - (dd->ipath_pbufsport * uports);
if (val32 > 0) {
ipath_dbg("allocating %u pbufs/port leaves %u unused, "
"add to kernel\n", dd->ipath_pbufsport, val32);
@@ -754,8 +764,7 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
dd->ipath_lastpioindex = dd->ipath_lastport_piobuf;
ipath_cdbg(VERBOSE, "%d PIO bufs for kernel out of %d total %u "
"each for %u user ports\n", kpiobufs,
- dd->ipath_piobcnt2k + dd->ipath_piobcnt4k,
- dd->ipath_pbufsport, dd->ipath_cfgports - 1);
+ piobufs, dd->ipath_pbufsport, uports);
dd->ipath_f_early_init(dd);
@@ -839,11 +848,24 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
* Set up the port 0 (kernel) rcvhdr q and egr TIDs. If doing
* re-init, the simplest way to handle this is to free
* existing, and re-allocate.
+ * Need to re-create rest of port 0 portdata as well.
*/
if (reinit) {
- struct ipath_portdata *pd = dd->ipath_pd[0];
- dd->ipath_pd[0] = NULL;
- ipath_free_pddata(dd, pd);
+ /* Alloc and init new ipath_portdata for port0,
+ * Then free old pd. Could lead to fragmentation, but also
+ * makes later support for hot-swap easier.
+ */
+ struct ipath_portdata *npd;
+ npd = create_portdata0(dd);
+ if (npd) {
+ ipath_free_pddata(dd, pd);
+ dd->ipath_pd[0] = pd = npd;
+ } else {
+ ipath_dev_err(dd, "Unable to allocate portdata for"
+ " port 0, failing\n");
+ ret = -ENOMEM;
+ goto done;
+ }
}
dd->ipath_f_tidtemplate(dd);
ret = ipath_create_rcvhdrq(dd, pd);
diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c
index 72b9e279d19d..45d033169c6e 100644
--- a/drivers/infiniband/hw/ipath/ipath_intr.c
+++ b/drivers/infiniband/hw/ipath/ipath_intr.c
@@ -38,10 +38,39 @@
#include "ipath_common.h"
/*
+ * clear (write) a pio buffer, to clear a parity error. This routine
+ * should only be called when in freeze mode, and the buffer should be
+ * canceled afterwards.
+ */
+static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum)
+{
+ u32 __iomem *pbuf;
+ u32 dwcnt; /* dword count to write */
+ if (pnum < dd->ipath_piobcnt2k) {
+ pbuf = (u32 __iomem *) (dd->ipath_pio2kbase + pnum *
+ dd->ipath_palign);
+ dwcnt = dd->ipath_piosize2k >> 2;
+ }
+ else {
+ pbuf = (u32 __iomem *) (dd->ipath_pio4kbase +
+ (pnum - dd->ipath_piobcnt2k) * dd->ipath_4kalign);
+ dwcnt = dd->ipath_piosize4k >> 2;
+ }
+ dev_info(&dd->pcidev->dev,
+ "Rewrite PIO buffer %u, to recover from parity error\n",
+ pnum);
+ *pbuf = dwcnt+1; /* no flush required, since already in freeze */
+ while(--dwcnt)
+ *pbuf++ = 0;
+}
+
+/*
* 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.
+ * If rewrite is true, and bits are set in the sendbufferror registers,
+ * we'll write to the buffer, for error recovery on parity errors.
*/
-void ipath_disarm_senderrbufs(struct ipath_devdata *dd)
+void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite)
{
u32 piobcnt;
unsigned long sbuf[4];
@@ -74,8 +103,11 @@ void ipath_disarm_senderrbufs(struct ipath_devdata *dd)
}
for (i = 0; i < piobcnt; i++)
- if (test_bit(i, sbuf))
+ if (test_bit(i, sbuf)) {
+ if (rewrite)
+ ipath_clrpiobuf(dd, i);
ipath_disarm_piobufs(dd, i, 1);
+ }
dd->ipath_lastcancel = jiffies+3; /* no armlaunch for a bit */
}
}
@@ -114,7 +146,7 @@ static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs)
{
u64 ignore_this_time = 0;
- ipath_disarm_senderrbufs(dd);
+ ipath_disarm_senderrbufs(dd, 0);
if ((errs & E_SUM_LINK_PKTERRS) &&
!(dd->ipath_flags & IPATH_LINKACTIVE)) {
/*
@@ -403,10 +435,13 @@ static void handle_supp_msgs(struct ipath_devdata *dd,
* happens so often we never want to count it.
*/
if (dd->ipath_lasterror & ~INFINIPATH_E_IBSTATUSCHANGED) {
- ipath_decode_err(msg, sizeof msg, dd->ipath_lasterror &
- ~INFINIPATH_E_IBSTATUSCHANGED);
+ int iserr;
+ iserr = ipath_decode_err(msg, sizeof msg,
+ dd->ipath_lasterror &
+ ~INFINIPATH_E_IBSTATUSCHANGED);
if (dd->ipath_lasterror &
- ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
+ ~(INFINIPATH_E_RRCVEGRFULL |
+ INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS))
ipath_dev_err(dd, "Suppressed %u messages for "
"fast-repeating errors (%s) (%llx)\n",
supp_msgs, msg,
@@ -420,8 +455,13 @@ static void handle_supp_msgs(struct ipath_devdata *dd,
* them. So only complain about these at debug
* level.
*/
- ipath_dbg("Suppressed %u messages for %s\n",
- supp_msgs, msg);
+ if (iserr)
+ ipath_dbg("Suppressed %u messages for %s\n",
+ supp_msgs, msg);
+ else
+ ipath_cdbg(ERRPKT,
+ "Suppressed %u messages for %s\n",
+ supp_msgs, msg);
}
}
}
@@ -462,7 +502,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
{
char msg[512];
u64 ignore_this_time = 0;
- int i;
+ int i, iserr = 0;
int chkerrpkts = 0, noprint = 0;
unsigned supp_msgs;
@@ -502,6 +542,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
}
if (supp_msgs == 250000) {
+ int s_iserr;
/*
* It's not entirely reasonable assuming that the errors set
* in the last clear period are all responsible for the
@@ -511,17 +552,17 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
dd->ipath_maskederrs |= dd->ipath_lasterror | errs;
ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
~dd->ipath_maskederrs);
- ipath_decode_err(msg, sizeof msg,
+ s_iserr = ipath_decode_err(msg, sizeof msg,
(dd->ipath_maskederrs & ~dd->
ipath_ignorederrs));
if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) &
- ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
- ipath_dev_err(dd, "Disabling error(s) %llx because "
- "occurring too frequently (%s)\n",
- (unsigned long long)
- (dd->ipath_maskederrs &
- ~dd->ipath_ignorederrs), msg);
+ ~(INFINIPATH_E_RRCVEGRFULL |
+ INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS))
+ ipath_dev_err(dd, "Temporarily disabling "
+ "error(s) %llx reporting; too frequent (%s)\n",
+ (unsigned long long) (dd->ipath_maskederrs &
+ ~dd->ipath_ignorederrs), msg);
else {
/*
* rcvegrfull and rcvhdrqfull are "normal",
@@ -530,8 +571,15 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
* processing them. So only complain about
* these at debug level.
*/
- ipath_dbg("Disabling frequent queue full errors "
- "(%s)\n", msg);
+ if (s_iserr)
+ ipath_dbg("Temporarily disabling reporting "
+ "too frequent queue full errors (%s)\n",
+ msg);
+ else
+ ipath_cdbg(ERRPKT,
+ "Temporarily disabling reporting too"
+ " frequent packet errors (%s)\n",
+ msg);
}
/*
@@ -589,6 +637,8 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
ipath_stats.sps_crcerrs++;
chkerrpkts = 1;
}
+ iserr = errs & ~(E_SUM_PKTERRS | INFINIPATH_E_PKTERRS);
+
/*
* We don't want to print these two as they happen, or we can make
@@ -677,8 +727,13 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
*dd->ipath_statusp &= ~IPATH_STATUS_IB_CONF;
}
- if (!noprint && *msg)
- ipath_dev_err(dd, "%s error\n", msg);
+ if (!noprint && *msg) {
+ if (iserr)
+ ipath_dev_err(dd, "%s error\n", msg);
+ else
+ dev_info(&dd->pcidev->dev, "%s packet problems\n",
+ msg);
+ }
if (dd->ipath_state_wanted & dd->ipath_flags) {
ipath_cdbg(VERBOSE, "driver wanted state %x, iflags now %x, "
"waking\n", dd->ipath_state_wanted,
@@ -819,11 +874,10 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
struct ipath_portdata *pd = dd->ipath_pd[i];
if (portr & (1 << i) && pd && pd->port_cnt &&
test_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag)) {
- int rcbit;
clear_bit(IPATH_PORT_WAITING_RCV,
&pd->port_flag);
- rcbit = i + INFINIPATH_R_INTRAVAIL_SHIFT;
- clear_bit(1UL << rcbit, &dd->ipath_rcvctrl);
+ clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT,
+ &dd->ipath_rcvctrl);
wake_up_interruptible(&pd->port_wait);
rcvdint = 1;
}
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index 6d8d05fb5999..e900c2593f44 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -590,7 +590,6 @@ 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, const struct file_operations *fops,
@@ -611,7 +610,7 @@ struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd, gfp_t);
extern int ipath_diag_inuse;
irqreturn_t ipath_intr(int irq, void *devid);
-void ipath_decode_err(char *buf, size_t blen, ipath_err_t err);
+int ipath_decode_err(char *buf, size_t blen, ipath_err_t err);
#if __IPATH_INFO || __IPATH_DBG
extern const char *ipath_ibcstatus_str[];
#endif
@@ -701,6 +700,8 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
#define IPATH_PORT_WAITING_RCV 2
/* waiting for a PIO buffer to be available */
#define IPATH_PORT_WAITING_PIO 3
+ /* master has not finished initializing */
+#define IPATH_PORT_MASTER_UNINIT 4
/* free up any allocated data at closes */
void ipath_free_data(struct ipath_portdata *dd);
@@ -711,6 +712,7 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *);
void ipath_init_iba6110_funcs(struct ipath_devdata *);
void ipath_get_eeprom_info(struct ipath_devdata *);
u64 ipath_snap_cntr(struct ipath_devdata *, ipath_creg);
+void ipath_disarm_senderrbufs(struct ipath_devdata *, int);
/*
* number of words used for protocol header if not set by ipath_userinit();
@@ -754,8 +756,6 @@ int ipath_eeprom_write(struct ipath_devdata *, u8, const void *, int);
/* these are used for the registers that vary with port */
void ipath_write_kreg_port(const struct ipath_devdata *, ipath_kreg,
unsigned, u64);
-u64 ipath_read_kreg64_port(const struct ipath_devdata *, ipath_kreg,
- unsigned);
/*
* We could have a single register get/put routine, that takes a group type,
@@ -897,6 +897,8 @@ dma_addr_t ipath_map_single(struct pci_dev *, void *, size_t, int);
extern unsigned ipath_debug; /* debugging bit mask */
+#define IPATH_MAX_PARITY_ATTEMPTS 10000 /* max times to try recovery */
+
const char *ipath_get_unit_name(int unit);
extern struct mutex ipath_mutex;
diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c
index 851763d7d2db..dd487c100f5b 100644
--- a/drivers/infiniband/hw/ipath/ipath_keys.c
+++ b/drivers/infiniband/hw/ipath/ipath_keys.c
@@ -61,7 +61,7 @@ int ipath_alloc_lkey(struct ipath_lkey_table *rkt, struct ipath_mregion *mr)
r = (r + 1) & (rkt->max - 1);
if (r == n) {
spin_unlock_irqrestore(&rkt->lock, flags);
- ipath_dbg(KERN_INFO "LKEY table full\n");
+ ipath_dbg("LKEY table full\n");
ret = 0;
goto bail;
}
@@ -133,6 +133,12 @@ int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
* being reversible by calling bus_to_virt().
*/
if (sge->lkey == 0) {
+ struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
+
+ if (pd->user) {
+ ret = 0;
+ goto bail;
+ }
isge->mr = NULL;
isge->vaddr = (void *) sge->addr;
isge->length = sge->length;
@@ -206,6 +212,12 @@ int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
* (see ipath_get_dma_mr and ipath_dma.c).
*/
if (rkey == 0) {
+ struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
+
+ if (pd->user) {
+ ret = 0;
+ goto bail;
+ }
sge->mr = NULL;
sge->vaddr = (void *) vaddr;
sge->length = len;
diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c
index 8cc8598d6c69..31e70732e369 100644
--- a/drivers/infiniband/hw/ipath/ipath_mr.c
+++ b/drivers/infiniband/hw/ipath/ipath_mr.c
@@ -210,9 +210,15 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region,
m = 0;
n = 0;
list_for_each_entry(chunk, &region->chunk_list, list) {
- for (i = 0; i < chunk->nmap; i++) {
- mr->mr.map[m]->segs[n].vaddr =
- page_address(chunk->page_list[i].page);
+ for (i = 0; i < chunk->nents; i++) {
+ void *vaddr;
+
+ vaddr = page_address(chunk->page_list[i].page);
+ if (!vaddr) {
+ ret = ERR_PTR(-EINVAL);
+ goto bail;
+ }
+ mr->mr.map[m]->segs[n].vaddr = vaddr;
mr->mr.map[m]->segs[n].length = region->page_size;
n++;
if (n == IPATH_SEGSZ) {
diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c
index 64f07b19349f..16db9ac0b402 100644
--- a/drivers/infiniband/hw/ipath/ipath_qp.c
+++ b/drivers/infiniband/hw/ipath/ipath_qp.c
@@ -81,11 +81,51 @@ static u32 credit_table[31] = {
32768 /* 1E */
};
-static u32 alloc_qpn(struct ipath_qp_table *qpt)
+
+static void get_map_page(struct ipath_qp_table *qpt, struct qpn_map *map)
+{
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ unsigned long flags;
+
+ /*
+ * Free the page if someone raced with us installing it.
+ */
+
+ spin_lock_irqsave(&qpt->lock, flags);
+ if (map->page)
+ free_page(page);
+ else
+ map->page = (void *)page;
+ spin_unlock_irqrestore(&qpt->lock, flags);
+}
+
+
+static int alloc_qpn(struct ipath_qp_table *qpt, enum ib_qp_type type)
{
u32 i, offset, max_scan, qpn;
struct qpn_map *map;
- u32 ret;
+ u32 ret = -1;
+
+ if (type == IB_QPT_SMI)
+ ret = 0;
+ else if (type == IB_QPT_GSI)
+ ret = 1;
+
+ if (ret != -1) {
+ map = &qpt->map[0];
+ if (unlikely(!map->page)) {
+ get_map_page(qpt, map);
+ if (unlikely(!map->page)) {
+ ret = -ENOMEM;
+ goto bail;
+ }
+ }
+ if (!test_and_set_bit(ret, map->page))
+ atomic_dec(&map->n_free);
+ else
+ ret = -EBUSY;
+ goto bail;
+ }
qpn = qpt->last + 1;
if (qpn >= QPN_MAX)
@@ -95,19 +135,7 @@ static u32 alloc_qpn(struct ipath_qp_table *qpt)
max_scan = qpt->nmaps - !offset;
for (i = 0;;) {
if (unlikely(!map->page)) {
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- unsigned long flags;
-
- /*
- * Free the page if someone raced with us
- * installing it:
- */
- spin_lock_irqsave(&qpt->lock, flags);
- if (map->page)
- free_page(page);
- else
- map->page = (void *)page;
- spin_unlock_irqrestore(&qpt->lock, flags);
+ get_map_page(qpt, map);
if (unlikely(!map->page))
break;
}
@@ -151,7 +179,7 @@ static u32 alloc_qpn(struct ipath_qp_table *qpt)
qpn = mk_qpn(qpt, map, offset);
}
- ret = 0;
+ ret = -ENOMEM;
bail:
return ret;
@@ -180,29 +208,19 @@ static int ipath_alloc_qpn(struct ipath_qp_table *qpt, struct ipath_qp *qp,
enum ib_qp_type type)
{
unsigned long flags;
- u32 qpn;
int ret;
- if (type == IB_QPT_SMI)
- qpn = 0;
- else if (type == IB_QPT_GSI)
- qpn = 1;
- else {
- /* Allocate the next available QPN */
- qpn = alloc_qpn(qpt);
- if (qpn == 0) {
- ret = -ENOMEM;
- goto bail;
- }
- }
- qp->ibqp.qp_num = qpn;
+ ret = alloc_qpn(qpt, type);
+ if (ret < 0)
+ goto bail;
+ qp->ibqp.qp_num = ret;
/* Add the QP to the hash table. */
spin_lock_irqsave(&qpt->lock, flags);
- qpn %= qpt->max;
- qp->next = qpt->table[qpn];
- qpt->table[qpn] = qp;
+ ret %= qpt->max;
+ qp->next = qpt->table[ret];
+ qpt->table[ret] = qp;
atomic_inc(&qp->refcount);
spin_unlock_irqrestore(&qpt->lock, flags);
@@ -245,9 +263,7 @@ static void ipath_free_qp(struct ipath_qp_table *qpt, struct ipath_qp *qp)
if (!fnd)
return;
- /* If QPN is not reserved, mark QPN free in the bitmap. */
- if (qp->ibqp.qp_num > 1)
- free_qpn(qpt, qp->ibqp.qp_num);
+ free_qpn(qpt, qp->ibqp.qp_num);
wait_event(qp->wait, !atomic_read(&qp->refcount));
}
@@ -270,11 +286,10 @@ void ipath_free_all_qps(struct ipath_qp_table *qpt)
while (qp) {
nqp = qp->next;
- if (qp->ibqp.qp_num > 1)
- free_qpn(qpt, qp->ibqp.qp_num);
+ free_qpn(qpt, qp->ibqp.qp_num);
if (!atomic_dec_and_test(&qp->refcount) ||
!ipath_destroy_qp(&qp->ibqp))
- ipath_dbg(KERN_INFO "QP memory leak!\n");
+ ipath_dbg("QP memory leak!\n");
qp = nqp;
}
}
@@ -320,7 +335,8 @@ static void ipath_reset_qp(struct ipath_qp *qp)
qp->remote_qpn = 0;
qp->qkey = 0;
qp->qp_access_flags = 0;
- clear_bit(IPATH_S_BUSY, &qp->s_flags);
+ qp->s_busy = 0;
+ qp->s_flags &= ~IPATH_S_SIGNAL_REQ_WR;
qp->s_hdrwords = 0;
qp->s_psn = 0;
qp->r_psn = 0;
@@ -333,7 +349,6 @@ static void ipath_reset_qp(struct ipath_qp *qp)
qp->r_state = IB_OPCODE_UC_SEND_LAST;
}
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;
@@ -344,6 +359,10 @@ static void ipath_reset_qp(struct ipath_qp *qp)
qp->s_ssn = 1;
qp->s_lsn = 0;
qp->s_wait_credit = 0;
+ memset(qp->s_ack_queue, 0, sizeof(qp->s_ack_queue));
+ qp->r_head_ack_queue = 0;
+ qp->s_tail_ack_queue = 0;
+ qp->s_num_rd_atomic = 0;
if (qp->r_rq.wq) {
qp->r_rq.wq->head = 0;
qp->r_rq.wq->tail = 0;
@@ -357,7 +376,7 @@ static void ipath_reset_qp(struct ipath_qp *qp)
* @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.
+ * The QP s_lock should be held and interrupts disabled.
*/
void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err)
@@ -365,7 +384,7 @@ 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;
- ipath_dbg(KERN_INFO "QP%d/%d in error state\n",
+ ipath_dbg("QP%d/%d in error state\n",
qp->ibqp.qp_num, qp->remote_qpn);
spin_lock(&dev->pending_lock);
@@ -389,6 +408,8 @@ void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err)
wc.port_num = 0;
if (qp->r_wrid_valid) {
qp->r_wrid_valid = 0;
+ wc.wr_id = qp->r_wr_id;
+ wc.opcode = IB_WC_RECV;
wc.status = err;
ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1);
}
@@ -503,13 +524,17 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
attr->path_mig_state != IB_MIG_REARM)
goto inval;
+ if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
+ if (attr->max_dest_rd_atomic > IPATH_MAX_RDMA_ATOMIC)
+ goto inval;
+
switch (new_state) {
case IB_QPS_RESET:
ipath_reset_qp(qp);
break;
case IB_QPS_ERR:
- ipath_error_qp(qp, IB_WC_GENERAL_ERR);
+ ipath_error_qp(qp, IB_WC_WR_FLUSH_ERR);
break;
default:
@@ -559,6 +584,12 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
if (attr_mask & IB_QP_QKEY)
qp->qkey = attr->qkey;
+ if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
+ qp->r_max_rd_atomic = attr->max_dest_rd_atomic;
+
+ if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC)
+ qp->s_max_rd_atomic = attr->max_rd_atomic;
+
qp->state = new_state;
spin_unlock_irqrestore(&qp->s_lock, flags);
@@ -598,8 +629,8 @@ int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
attr->alt_pkey_index = 0;
attr->en_sqd_async_notify = 0;
attr->sq_draining = 0;
- attr->max_rd_atomic = 1;
- attr->max_dest_rd_atomic = 1;
+ attr->max_rd_atomic = qp->s_max_rd_atomic;
+ attr->max_dest_rd_atomic = qp->r_max_rd_atomic;
attr->min_rnr_timer = qp->r_min_rnr_timer;
attr->port_num = 1;
attr->timeout = qp->timeout;
@@ -614,7 +645,7 @@ int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
init_attr->recv_cq = qp->ibqp.recv_cq;
init_attr->srq = qp->ibqp.srq;
init_attr->cap = attr->cap;
- if (qp->s_flags & (1 << IPATH_S_SIGNAL_REQ_WR))
+ if (qp->s_flags & IPATH_S_SIGNAL_REQ_WR)
init_attr->sq_sig_type = IB_SIGNAL_REQ_WR;
else
init_attr->sq_sig_type = IB_SIGNAL_ALL_WR;
@@ -786,7 +817,7 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
qp->s_size = init_attr->cap.max_send_wr + 1;
qp->s_max_sge = init_attr->cap.max_send_sge;
if (init_attr->sq_sig_type == IB_SIGNAL_REQ_WR)
- qp->s_flags = 1 << IPATH_S_SIGNAL_REQ_WR;
+ qp->s_flags = IPATH_S_SIGNAL_REQ_WR;
else
qp->s_flags = 0;
dev = to_idev(ibpd->device);
@@ -958,7 +989,7 @@ bail:
* @wc: the WC responsible for putting the QP in this state
*
* Flushes the send work queue.
- * The QP s_lock should be held.
+ * The QP s_lock should be held and interrupts disabled.
*/
void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc)
@@ -966,7 +997,7 @@ void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc)
struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last);
- ipath_dbg(KERN_INFO "Send queue error on QP%d/%d: err: %d\n",
+ ipath_dbg("Send queue error on QP%d/%d: err: %d\n",
qp->ibqp.qp_num, qp->remote_qpn, wc->status);
spin_lock(&dev->pending_lock);
@@ -984,12 +1015,12 @@ void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc)
wc->status = IB_WC_WR_FLUSH_ERR;
while (qp->s_last != qp->s_head) {
+ wqe = get_swqe_ptr(qp, qp->s_last);
wc->wr_id = wqe->wr.wr_id;
wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 1);
if (++qp->s_last >= qp->s_size)
qp->s_last = 0;
- wqe = get_swqe_ptr(qp, qp->s_last);
}
qp->s_cur = qp->s_tail = qp->s_head;
qp->state = IB_QPS_SQE;
diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c
index 5ff20cb04494..b4b88d0b53f5 100644
--- a/drivers/infiniband/hw/ipath/ipath_rc.c
+++ b/drivers/infiniband/hw/ipath/ipath_rc.c
@@ -37,6 +37,19 @@
/* cut down ridiculously long IB macro names */
#define OP(x) IB_OPCODE_RC_##x
+static u32 restart_sge(struct ipath_sge_state *ss, struct ipath_swqe *wqe,
+ u32 psn, u32 pmtu)
+{
+ u32 len;
+
+ len = ((psn - wqe->psn) & IPATH_PSN_MASK) * pmtu;
+ ss->sge = wqe->sg_list[0];
+ ss->sg_list = wqe->sg_list + 1;
+ ss->num_sge = wqe->wr.num_sge;
+ ipath_skip_sge(ss, len);
+ return wqe->length - len;
+}
+
/**
* ipath_init_restart- initialize the qp->s_sge after a restart
* @qp: the QP who's SGE we're restarting
@@ -47,15 +60,9 @@
static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe)
{
struct ipath_ibdev *dev;
- u32 len;
- len = ((qp->s_psn - wqe->psn) & IPATH_PSN_MASK) *
- ib_mtu_enum_to_int(qp->path_mtu);
- 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;
- ipath_skip_sge(&qp->s_sge, len);
- qp->s_len = wqe->length - len;
+ qp->s_len = restart_sge(&qp->s_sge, wqe, qp->s_psn,
+ ib_mtu_enum_to_int(qp->path_mtu));
dev = to_idev(qp->ibqp.device);
spin_lock(&dev->pending_lock);
if (list_empty(&qp->timerwait))
@@ -70,107 +77,123 @@ static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe)
* @ohdr: a pointer to the IB header being constructed
* @pmtu: the path MTU
*
- * Return bth0 if constructed; otherwise, return 0.
+ * Return 1 if constructed; otherwise, return 0.
+ * Note that we are in the responder's side of the QP context.
* Note the QP s_lock must be held.
*/
-u32 ipath_make_rc_ack(struct ipath_qp *qp,
- struct ipath_other_headers *ohdr,
- u32 pmtu)
+static int ipath_make_rc_ack(struct ipath_qp *qp,
+ struct ipath_other_headers *ohdr,
+ u32 pmtu, u32 *bth0p, u32 *bth2p)
{
+ struct ipath_ack_entry *e;
u32 hwords;
u32 len;
u32 bth0;
+ u32 bth2;
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
hwords = 5;
- /*
- * Send a response. Note that we are in the responder's
- * side of the QP context.
- */
switch (qp->s_ack_state) {
- case OP(RDMA_READ_REQUEST):
- qp->s_cur_sge = &qp->s_rdma_sge;
- len = qp->s_rdma_len;
- if (len > pmtu) {
- len = pmtu;
- qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST);
- } else
- qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY);
- qp->s_rdma_len -= len;
+ case OP(RDMA_READ_RESPONSE_LAST):
+ case OP(RDMA_READ_RESPONSE_ONLY):
+ case OP(ATOMIC_ACKNOWLEDGE):
+ qp->s_ack_state = OP(ACKNOWLEDGE);
+ /* FALLTHROUGH */
+ case OP(ACKNOWLEDGE):
+ /* Check for no next entry in the queue. */
+ if (qp->r_head_ack_queue == qp->s_tail_ack_queue) {
+ if (qp->s_flags & IPATH_S_ACK_PENDING)
+ goto normal;
+ goto bail;
+ }
+
+ e = &qp->s_ack_queue[qp->s_tail_ack_queue];
+ if (e->opcode == OP(RDMA_READ_REQUEST)) {
+ /* Copy SGE state in case we need to resend */
+ qp->s_ack_rdma_sge = e->rdma_sge;
+ qp->s_cur_sge = &qp->s_ack_rdma_sge;
+ len = e->rdma_sge.sge.sge_length;
+ if (len > pmtu) {
+ len = pmtu;
+ qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST);
+ } else {
+ qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY);
+ if (++qp->s_tail_ack_queue >
+ IPATH_MAX_RDMA_ATOMIC)
+ qp->s_tail_ack_queue = 0;
+ }
+ ohdr->u.aeth = ipath_compute_aeth(qp);
+ hwords++;
+ qp->s_ack_rdma_psn = e->psn;
+ bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK;
+ } else {
+ /* COMPARE_SWAP or FETCH_ADD */
+ qp->s_cur_sge = NULL;
+ len = 0;
+ qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE);
+ ohdr->u.at.aeth = ipath_compute_aeth(qp);
+ ohdr->u.at.atomic_ack_eth[0] =
+ cpu_to_be32(e->atomic_data >> 32);
+ ohdr->u.at.atomic_ack_eth[1] =
+ cpu_to_be32(e->atomic_data);
+ hwords += sizeof(ohdr->u.at) / sizeof(u32);
+ bth2 = e->psn;
+ if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC)
+ qp->s_tail_ack_queue = 0;
+ }
bth0 = qp->s_ack_state << 24;
- ohdr->u.aeth = ipath_compute_aeth(qp);
- hwords++;
break;
case OP(RDMA_READ_RESPONSE_FIRST):
qp->s_ack_state = OP(RDMA_READ_RESPONSE_MIDDLE);
/* FALLTHROUGH */
case OP(RDMA_READ_RESPONSE_MIDDLE):
- qp->s_cur_sge = &qp->s_rdma_sge;
- len = qp->s_rdma_len;
+ len = qp->s_ack_rdma_sge.sge.sge_length;
if (len > pmtu)
len = pmtu;
else {
ohdr->u.aeth = ipath_compute_aeth(qp);
hwords++;
qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
+ if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC)
+ qp->s_tail_ack_queue = 0;
}
- qp->s_rdma_len -= len;
bth0 = qp->s_ack_state << 24;
- break;
-
- case OP(RDMA_READ_RESPONSE_LAST):
- case OP(RDMA_READ_RESPONSE_ONLY):
- /*
- * We have to prevent new requests from changing
- * the r_sge state while a ipath_verbs_send()
- * is in progress.
- */
- qp->s_ack_state = OP(ACKNOWLEDGE);
- bth0 = 0;
- goto bail;
-
- case OP(COMPARE_SWAP):
- case OP(FETCH_ADD):
- qp->s_cur_sge = NULL;
- len = 0;
- /*
- * Set the s_ack_state so the receive interrupt handler
- * won't try to send an ACK (out of order) until this one
- * is actually sent.
- */
- qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
- bth0 = OP(ATOMIC_ACKNOWLEDGE) << 24;
- ohdr->u.at.aeth = ipath_compute_aeth(qp);
- ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data);
- hwords += sizeof(ohdr->u.at) / 4;
+ bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK;
break;
default:
- /* Send a regular ACK. */
- qp->s_cur_sge = NULL;
- len = 0;
+ normal:
/*
- * Set the s_ack_state so the receive interrupt handler
- * won't try to send an ACK (out of order) until this one
- * is actually sent.
+ * Send a regular ACK.
+ * Set the s_ack_state so we wait until after sending
+ * the ACK before setting s_ack_state to ACKNOWLEDGE
+ * (see above).
*/
- qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
- bth0 = OP(ACKNOWLEDGE) << 24;
+ qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE);
+ qp->s_flags &= ~IPATH_S_ACK_PENDING;
+ qp->s_cur_sge = NULL;
if (qp->s_nak_state)
- ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
- (qp->s_nak_state <<
- IPATH_AETH_CREDIT_SHIFT));
+ ohdr->u.aeth =
+ cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
+ (qp->s_nak_state <<
+ IPATH_AETH_CREDIT_SHIFT));
else
ohdr->u.aeth = ipath_compute_aeth(qp);
hwords++;
+ len = 0;
+ bth0 = OP(ACKNOWLEDGE) << 24;
+ bth2 = qp->s_ack_psn & IPATH_PSN_MASK;
}
qp->s_hdrwords = hwords;
qp->s_cur_size = len;
+ *bth0p = bth0;
+ *bth2p = bth2;
+ return 1;
bail:
- return bth0;
+ return 0;
}
/**
@@ -197,9 +220,16 @@ int ipath_make_rc_req(struct ipath_qp *qp,
u32 bth2;
char newreq;
+ /* Sending responses has higher priority over sending requests. */
+ if ((qp->r_head_ack_queue != qp->s_tail_ack_queue ||
+ (qp->s_flags & IPATH_S_ACK_PENDING) ||
+ qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE) &&
+ ipath_make_rc_ack(qp, ohdr, pmtu, bth0p, bth2p))
+ goto done;
+
if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK) ||
qp->s_rnr_timeout)
- goto done;
+ goto bail;
/* Limit the number of packets sent without an ACK. */
if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT) > 0) {
@@ -210,7 +240,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
list_add_tail(&qp->timerwait,
&dev->pending[dev->pending_index]);
spin_unlock(&dev->pending_lock);
- goto done;
+ goto bail;
}
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
@@ -232,7 +262,16 @@ int ipath_make_rc_req(struct ipath_qp *qp,
if (qp->s_cur == qp->s_tail) {
/* Check if send work queue is empty. */
if (qp->s_tail == qp->s_head)
- goto done;
+ goto bail;
+ /*
+ * If a fence is requested, wait for previous
+ * RDMA read and atomic operations to finish.
+ */
+ if ((wqe->wr.send_flags & IB_SEND_FENCE) &&
+ qp->s_num_rd_atomic) {
+ qp->s_flags |= IPATH_S_FENCE_PENDING;
+ goto bail;
+ }
wqe->psn = qp->s_next_psn;
newreq = 1;
}
@@ -250,7 +289,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
/* If no credit, return. */
if (qp->s_lsn != (u32) -1 &&
ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0)
- goto done;
+ goto bail;
wqe->lpsn = wqe->psn;
if (len > pmtu) {
wqe->lpsn += (len - 1) / pmtu;
@@ -281,13 +320,13 @@ int ipath_make_rc_req(struct ipath_qp *qp,
/* If no credit, return. */
if (qp->s_lsn != (u32) -1 &&
ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0)
- goto done;
+ goto bail;
ohdr->u.rc.reth.vaddr =
cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
ohdr->u.rc.reth.rkey =
cpu_to_be32(wqe->wr.wr.rdma.rkey);
ohdr->u.rc.reth.length = cpu_to_be32(len);
- hwords += sizeof(struct ib_reth) / 4;
+ hwords += sizeof(struct ib_reth) / sizeof(u32);
wqe->lpsn = wqe->psn;
if (len > pmtu) {
wqe->lpsn += (len - 1) / pmtu;
@@ -312,14 +351,17 @@ int ipath_make_rc_req(struct ipath_qp *qp,
break;
case IB_WR_RDMA_READ:
- ohdr->u.rc.reth.vaddr =
- cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
- ohdr->u.rc.reth.rkey =
- cpu_to_be32(wqe->wr.wr.rdma.rkey);
- ohdr->u.rc.reth.length = cpu_to_be32(len);
- qp->s_state = OP(RDMA_READ_REQUEST);
- hwords += sizeof(ohdr->u.rc.reth) / 4;
+ /*
+ * Don't allow more operations to be started
+ * than the QP limits allow.
+ */
if (newreq) {
+ if (qp->s_num_rd_atomic >=
+ qp->s_max_rd_atomic) {
+ qp->s_flags |= IPATH_S_RDMAR_PENDING;
+ goto bail;
+ }
+ qp->s_num_rd_atomic++;
if (qp->s_lsn != (u32) -1)
qp->s_lsn++;
/*
@@ -330,6 +372,13 @@ int ipath_make_rc_req(struct ipath_qp *qp,
qp->s_next_psn += (len - 1) / pmtu;
wqe->lpsn = qp->s_next_psn++;
}
+ ohdr->u.rc.reth.vaddr =
+ cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
+ ohdr->u.rc.reth.rkey =
+ cpu_to_be32(wqe->wr.wr.rdma.rkey);
+ ohdr->u.rc.reth.length = cpu_to_be32(len);
+ qp->s_state = OP(RDMA_READ_REQUEST);
+ hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32);
ss = NULL;
len = 0;
if (++qp->s_cur == qp->s_size)
@@ -338,32 +387,48 @@ int ipath_make_rc_req(struct ipath_qp *qp,
case IB_WR_ATOMIC_CMP_AND_SWP:
case IB_WR_ATOMIC_FETCH_AND_ADD:
- if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP)
- qp->s_state = OP(COMPARE_SWAP);
- else
- qp->s_state = OP(FETCH_ADD);
- ohdr->u.atomic_eth.vaddr = cpu_to_be64(
- wqe->wr.wr.atomic.remote_addr);
- ohdr->u.atomic_eth.rkey = cpu_to_be32(
- wqe->wr.wr.atomic.rkey);
- ohdr->u.atomic_eth.swap_data = cpu_to_be64(
- wqe->wr.wr.atomic.swap);
- ohdr->u.atomic_eth.compare_data = cpu_to_be64(
- wqe->wr.wr.atomic.compare_add);
- hwords += sizeof(struct ib_atomic_eth) / 4;
+ /*
+ * Don't allow more operations to be started
+ * than the QP limits allow.
+ */
if (newreq) {
+ if (qp->s_num_rd_atomic >=
+ qp->s_max_rd_atomic) {
+ qp->s_flags |= IPATH_S_RDMAR_PENDING;
+ goto bail;
+ }
+ qp->s_num_rd_atomic++;
if (qp->s_lsn != (u32) -1)
qp->s_lsn++;
wqe->lpsn = wqe->psn;
}
- if (++qp->s_cur == qp->s_size)
- qp->s_cur = 0;
+ if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) {
+ qp->s_state = OP(COMPARE_SWAP);
+ ohdr->u.atomic_eth.swap_data = cpu_to_be64(
+ wqe->wr.wr.atomic.swap);
+ ohdr->u.atomic_eth.compare_data = cpu_to_be64(
+ wqe->wr.wr.atomic.compare_add);
+ } else {
+ qp->s_state = OP(FETCH_ADD);
+ ohdr->u.atomic_eth.swap_data = cpu_to_be64(
+ wqe->wr.wr.atomic.compare_add);
+ ohdr->u.atomic_eth.compare_data = 0;
+ }
+ ohdr->u.atomic_eth.vaddr[0] = cpu_to_be32(
+ wqe->wr.wr.atomic.remote_addr >> 32);
+ ohdr->u.atomic_eth.vaddr[1] = cpu_to_be32(
+ wqe->wr.wr.atomic.remote_addr);
+ ohdr->u.atomic_eth.rkey = cpu_to_be32(
+ wqe->wr.wr.atomic.rkey);
+ hwords += sizeof(struct ib_atomic_eth) / sizeof(u32);
ss = NULL;
len = 0;
+ if (++qp->s_cur == qp->s_size)
+ qp->s_cur = 0;
break;
default:
- goto done;
+ goto bail;
}
qp->s_sge.sge = wqe->sg_list[0];
qp->s_sge.sg_list = wqe->sg_list + 1;
@@ -379,7 +444,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
qp->s_psn = wqe->lpsn + 1;
else {
qp->s_psn++;
- if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+ if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
qp->s_next_psn = qp->s_psn;
}
/*
@@ -406,7 +471,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
/* FALLTHROUGH */
case OP(SEND_MIDDLE):
bth2 = qp->s_psn++ & IPATH_PSN_MASK;
- if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+ if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
qp->s_next_psn = qp->s_psn;
ss = &qp->s_sge;
len = qp->s_len;
@@ -442,7 +507,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
/* FALLTHROUGH */
case OP(RDMA_WRITE_MIDDLE):
bth2 = qp->s_psn++ & IPATH_PSN_MASK;
- if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+ if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
qp->s_next_psn = qp->s_psn;
ss = &qp->s_sge;
len = qp->s_len;
@@ -479,9 +544,9 @@ int ipath_make_rc_req(struct ipath_qp *qp,
cpu_to_be32(wqe->wr.wr.rdma.rkey);
ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len);
qp->s_state = OP(RDMA_READ_REQUEST);
- hwords += sizeof(ohdr->u.rc.reth) / 4;
+ hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32);
bth2 = qp->s_psn++ & IPATH_PSN_MASK;
- if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+ if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
qp->s_next_psn = qp->s_psn;
ss = NULL;
len = 0;
@@ -489,20 +554,6 @@ int ipath_make_rc_req(struct ipath_qp *qp,
if (qp->s_cur == qp->s_size)
qp->s_cur = 0;
break;
-
- case OP(RDMA_READ_REQUEST):
- case OP(COMPARE_SWAP):
- case OP(FETCH_ADD):
- /*
- * We shouldn't start anything new until this request is
- * finished. The ACK will handle rescheduling us. XXX The
- * number of outstanding ones is negotiated at connection
- * setup time (see pg. 258,289)? XXX Also, if we support
- * multiple outstanding requests, we need to check the WQE
- * IB_SEND_FENCE flag and not send a new request if a RDMA
- * read or atomic is pending.
- */
- goto done;
}
if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0)
bth2 |= 1 << 31; /* Request ACK. */
@@ -512,9 +563,10 @@ int ipath_make_rc_req(struct ipath_qp *qp,
qp->s_cur_size = len;
*bth0p = bth0 | (qp->s_state << 24);
*bth2p = bth2;
+done:
return 1;
-done:
+bail:
return 0;
}
@@ -524,7 +576,8 @@ done:
*
* This is called from ipath_rc_rcv() and only uses the receive
* side QP state.
- * Note that RDMA reads are handled in the send side QP state and tasklet.
+ * Note that RDMA reads and atomics are handled in the
+ * send side QP state and tasklet.
*/
static void send_rc_ack(struct ipath_qp *qp)
{
@@ -535,6 +588,10 @@ static void send_rc_ack(struct ipath_qp *qp)
struct ipath_ib_header hdr;
struct ipath_other_headers *ohdr;
+ /* Don't send ACK or NAK if a RDMA read or atomic is pending. */
+ if (qp->r_head_ack_queue != qp->s_tail_ack_queue)
+ goto queue_ack;
+
/* Construct the header. */
ohdr = &hdr.u.oth;
lrh0 = IPATH_LRH_BTH;
@@ -548,19 +605,14 @@ static void send_rc_ack(struct ipath_qp *qp)
lrh0 = IPATH_LRH_GRH;
}
/* read pkey_index w/o lock (its atomic) */
- bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index);
+ bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index) |
+ OP(ACKNOWLEDGE) << 24;
if (qp->r_nak_state)
ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
(qp->r_nak_state <<
IPATH_AETH_CREDIT_SHIFT));
else
ohdr->u.aeth = ipath_compute_aeth(qp);
- if (qp->r_ack_state >= OP(COMPARE_SWAP)) {
- bth0 |= OP(ATOMIC_ACKNOWLEDGE) << 24;
- ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data);
- hwords += sizeof(ohdr->u.at.atomic_ack_eth) / 4;
- } else
- bth0 |= OP(ACKNOWLEDGE) << 24;
lrh0 |= qp->remote_ah_attr.sl << 4;
hdr.lrh[0] = cpu_to_be16(lrh0);
hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid);
@@ -574,31 +626,31 @@ static void send_rc_ack(struct ipath_qp *qp)
* If we can send the ACK, clear the ACK state.
*/
if (ipath_verbs_send(dev->dd, hwords, (u32 *) &hdr, 0, NULL) == 0) {
- qp->r_ack_state = OP(ACKNOWLEDGE);
dev->n_unicast_xmit++;
- } else {
- /*
- * We are out of PIO buffers at the moment.
- * Pass responsibility for sending the ACK to the
- * send tasklet so that when a PIO buffer becomes
- * available, the ACK is sent ahead of other outgoing
- * packets.
- */
- dev->n_rc_qacks++;
- spin_lock_irq(&qp->s_lock);
- /* Don't coalesce if a RDMA read or atomic is pending. */
- if (qp->s_ack_state == OP(ACKNOWLEDGE) ||
- qp->s_ack_state < OP(RDMA_READ_REQUEST)) {
- qp->s_ack_state = qp->r_ack_state;
- qp->s_nak_state = qp->r_nak_state;
- qp->s_ack_psn = qp->r_ack_psn;
- qp->r_ack_state = OP(ACKNOWLEDGE);
- }
- spin_unlock_irq(&qp->s_lock);
-
- /* Call ipath_do_rc_send() in another thread. */
- tasklet_hi_schedule(&qp->s_task);
+ goto done;
}
+
+ /*
+ * We are out of PIO buffers at the moment.
+ * Pass responsibility for sending the ACK to the
+ * send tasklet so that when a PIO buffer becomes
+ * available, the ACK is sent ahead of other outgoing
+ * packets.
+ */
+ dev->n_rc_qacks++;
+
+queue_ack:
+ spin_lock_irq(&qp->s_lock);
+ qp->s_flags |= IPATH_S_ACK_PENDING;
+ qp->s_nak_state = qp->r_nak_state;
+ qp->s_ack_psn = qp->r_ack_psn;
+ spin_unlock_irq(&qp->s_lock);
+
+ /* Call ipath_do_rc_send() in another thread. */
+ tasklet_hi_schedule(&qp->s_task);
+
+done:
+ return;
}
/**
@@ -727,7 +779,7 @@ void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc)
if (wqe->wr.opcode == IB_WR_RDMA_READ)
dev->n_rc_resends++;
else
- dev->n_rc_resends += (int)qp->s_psn - (int)psn;
+ dev->n_rc_resends += (qp->s_psn - psn) & IPATH_PSN_MASK;
reset_psn(qp, psn);
tasklet_hi_schedule(&qp->s_task);
@@ -775,10 +827,6 @@ 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
@@ -806,7 +854,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
*/
if ((wqe->wr.opcode == IB_WR_RDMA_READ &&
(opcode != OP(RDMA_READ_RESPONSE_LAST) ||
- ipath_cmp24(ack_psn, wqe->lpsn) != 0)) ||
+ 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) ||
@@ -824,20 +872,33 @@ 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);
+ if (qp->s_num_rd_atomic &&
+ (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)) {
+ qp->s_num_rd_atomic--;
+ /* Restart sending task if fence is complete */
+ if ((qp->s_flags & IPATH_S_FENCE_PENDING) &&
+ !qp->s_num_rd_atomic) {
+ qp->s_flags &= ~IPATH_S_FENCE_PENDING;
+ tasklet_hi_schedule(&qp->s_task);
+ } else if (qp->s_flags & IPATH_S_RDMAR_PENDING) {
+ qp->s_flags &= ~IPATH_S_RDMAR_PENDING;
+ 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) ||
+ if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
(wqe->wr.send_flags & IB_SEND_SIGNALED)) {
wc.wr_id = wqe->wr.wr_id;
wc.status = IB_WC_SUCCESS;
wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
wc.vendor_err = 0;
wc.byte_len = wqe->length;
+ wc.imm_data = 0;
wc.qp = &qp->ibqp;
wc.src_qp = qp->remote_qpn;
+ wc.wc_flags = 0;
wc.pkey_index = 0;
wc.slid = qp->remote_ah_attr.dlid;
wc.sl = qp->remote_ah_attr.sl;
@@ -854,15 +915,19 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
if (qp->s_last == qp->s_cur) {
if (++qp->s_cur >= qp->s_size)
qp->s_cur = 0;
+ qp->s_last = qp->s_cur;
+ if (qp->s_last == qp->s_tail)
+ break;
wqe = get_swqe_ptr(qp, qp->s_cur);
qp->s_state = OP(SEND_LAST);
qp->s_psn = wqe->psn;
+ } else {
+ if (++qp->s_last >= qp->s_size)
+ qp->s_last = 0;
+ if (qp->s_last == qp->s_tail)
+ break;
+ wqe = get_swqe_ptr(qp, qp->s_last);
}
- if (++qp->s_last >= qp->s_size)
- qp->s_last = 0;
- wqe = get_swqe_ptr(qp, qp->s_last);
- if (qp->s_last == qp->s_tail)
- break;
}
switch (aeth >> 29) {
@@ -874,6 +939,18 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
list_add_tail(&qp->timerwait,
&dev->pending[dev->pending_index]);
spin_unlock(&dev->pending_lock);
+ /*
+ * If we get a partial ACK for a resent operation,
+ * we can stop resending the earlier packets and
+ * continue with the next packet the receiver wants.
+ */
+ if (ipath_cmp24(qp->s_psn, psn) <= 0) {
+ reset_psn(qp, psn + 1);
+ tasklet_hi_schedule(&qp->s_task);
+ }
+ } else if (ipath_cmp24(qp->s_psn, psn) <= 0) {
+ qp->s_state = OP(SEND_LAST);
+ qp->s_psn = psn + 1;
}
ipath_get_credit(qp, aeth);
qp->s_rnr_retry = qp->s_rnr_retry_cnt;
@@ -884,22 +961,23 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
case 1: /* RNR NAK */
dev->n_rnr_naks++;
+ if (qp->s_last == qp->s_tail)
+ goto bail;
if (qp->s_rnr_retry == 0) {
- if (qp->s_last == qp->s_tail)
- goto bail;
-
wc.status = IB_WC_RNR_RETRY_EXC_ERR;
goto class_b;
}
if (qp->s_rnr_retry_cnt < 7)
qp->s_rnr_retry--;
- if (qp->s_last == qp->s_tail)
- goto bail;
/* The last valid PSN is the previous PSN. */
update_last_psn(qp, psn - 1);
- dev->n_rc_resends += (int)qp->s_psn - (int)psn;
+ if (wqe->wr.opcode == IB_WR_RDMA_READ)
+ dev->n_rc_resends++;
+ else
+ dev->n_rc_resends +=
+ (qp->s_psn - psn) & IPATH_PSN_MASK;
reset_psn(qp, psn);
@@ -910,26 +988,20 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
goto bail;
case 3: /* NAK */
- /* The last valid PSN seen is the previous request's. */
- if (qp->s_last != qp->s_tail)
- update_last_psn(qp, wqe->psn - 1);
+ if (qp->s_last == qp->s_tail)
+ goto bail;
+ /* The last valid PSN is the previous PSN. */
+ update_last_psn(qp, psn - 1);
switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) &
IPATH_AETH_CREDIT_MASK) {
case 0: /* PSN sequence error */
dev->n_seq_naks++;
/*
- * Back up to the responder's expected PSN. XXX
+ * Back up to the responder's expected PSN.
* Note that we might get a NAK in the middle of an
* RDMA READ response which terminates the RDMA
* READ.
*/
- if (qp->s_last == qp->s_tail)
- break;
-
- if (ipath_cmp24(psn, wqe->psn) < 0)
- break;
-
- /* Retry the request. */
ipath_restart_rc(qp, psn, &wc);
break;
@@ -1003,6 +1075,7 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
u32 psn, u32 hdrsize, u32 pmtu,
int header_in_data)
{
+ struct ipath_swqe *wqe;
unsigned long flags;
struct ib_wc wc;
int diff;
@@ -1032,6 +1105,10 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
goto ack_done;
}
+ if (unlikely(qp->s_last == qp->s_tail))
+ goto ack_done;
+ wqe = get_swqe_ptr(qp, qp->s_last);
+
switch (opcode) {
case OP(ACKNOWLEDGE):
case OP(ATOMIC_ACKNOWLEDGE):
@@ -1042,38 +1119,49 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
aeth = be32_to_cpu(((__be32 *) data)[0]);
data += sizeof(__be32);
}
- if (opcode == OP(ATOMIC_ACKNOWLEDGE))
- *(u64 *) qp->s_sge.sge.vaddr = *(u64 *) data;
+ if (opcode == OP(ATOMIC_ACKNOWLEDGE)) {
+ u64 val;
+
+ if (!header_in_data) {
+ __be32 *p = ohdr->u.at.atomic_ack_eth;
+
+ val = ((u64) be32_to_cpu(p[0]) << 32) |
+ be32_to_cpu(p[1]);
+ } else
+ val = be64_to_cpu(((__be64 *) data)[0]);
+ *(u64 *) wqe->sg_list[0].vaddr = val;
+ }
if (!do_rc_ack(qp, aeth, psn, opcode) ||
opcode != OP(RDMA_READ_RESPONSE_FIRST))
goto ack_done;
hdrsize += 4;
+ if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
+ goto ack_op_err;
/*
- * do_rc_ack() has already checked the PSN so skip
- * the sequence check.
+ * If this is a response to a resent RDMA read, we
+ * have to be careful to copy the data to the right
+ * location.
*/
- goto rdma_read;
+ qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge,
+ wqe, psn, pmtu);
+ goto read_middle;
case OP(RDMA_READ_RESPONSE_MIDDLE):
/* no AETH, no ACK */
if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
dev->n_rdma_seq++;
- if (qp->s_last != qp->s_tail)
- ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+ ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
goto ack_done;
}
- rdma_read:
- if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST)))
- goto ack_done;
+ if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
+ goto ack_op_err;
+ read_middle:
if (unlikely(tlen != (hdrsize + pmtu + 4)))
- goto ack_done;
- if (unlikely(pmtu >= qp->s_len))
- goto ack_done;
+ goto ack_len_err;
+ if (unlikely(pmtu >= qp->s_rdma_read_len))
+ goto ack_len_err;
+
/* We got a response so update the timeout. */
- if (unlikely(qp->s_last == qp->s_tail ||
- get_swqe_ptr(qp, qp->s_last)->wr.opcode !=
- IB_WR_RDMA_READ))
- goto ack_done;
spin_lock(&dev->pending_lock);
if (qp->s_rnr_timeout == 0 && !list_empty(&qp->timerwait))
list_move_tail(&qp->timerwait,
@@ -1082,67 +1170,97 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
/*
* Update the RDMA receive state but do the copy w/o
* holding the locks and blocking interrupts.
- * XXX Yet another place that affects relaxed RDMA order
- * since we don't want s_sge modified.
*/
- qp->s_len -= pmtu;
+ qp->s_rdma_read_len -= pmtu;
update_last_psn(qp, psn);
spin_unlock_irqrestore(&qp->s_lock, flags);
- ipath_copy_sge(&qp->s_sge, data, pmtu);
+ ipath_copy_sge(&qp->s_rdma_read_sge, data, pmtu);
goto bail;
- case OP(RDMA_READ_RESPONSE_LAST):
- /* ACKs READ req. */
+ case OP(RDMA_READ_RESPONSE_ONLY):
if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
dev->n_rdma_seq++;
- if (qp->s_last != qp->s_tail)
- ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+ ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
goto ack_done;
}
- /* FALLTHROUGH */
- case OP(RDMA_READ_RESPONSE_ONLY):
- if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST)))
- goto ack_done;
+ if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
+ goto ack_op_err;
+ /* Get the number of bytes the message was padded by. */
+ pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
+ /*
+ * Check that the data size is >= 0 && <= pmtu.
+ * Remember to account for the AETH header (4) and
+ * ICRC (4).
+ */
+ if (unlikely(tlen < (hdrsize + pad + 8)))
+ goto ack_len_err;
/*
- * Get the number of bytes the message was padded by.
+ * If this is a response to a resent RDMA read, we
+ * have to be careful to copy the data to the right
+ * location.
*/
+ qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge,
+ wqe, psn, pmtu);
+ goto read_last;
+
+ case OP(RDMA_READ_RESPONSE_LAST):
+ /* 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);
+ goto ack_done;
+ }
+ if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
+ goto ack_op_err;
+ /* Get the number of bytes the message was padded by. */
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
/*
* Check that the data size is >= 1 && <= pmtu.
* Remember to account for the AETH header (4) and
* ICRC (4).
*/
- if (unlikely(tlen <= (hdrsize + pad + 8))) {
- /* XXX Need to generate an error CQ entry. */
- goto ack_done;
- }
+ if (unlikely(tlen <= (hdrsize + pad + 8)))
+ goto ack_len_err;
+ read_last:
tlen -= hdrsize + pad + 8;
- if (unlikely(tlen != qp->s_len)) {
- /* XXX Need to generate an error CQ entry. */
- goto ack_done;
- }
+ if (unlikely(tlen != qp->s_rdma_read_len))
+ goto ack_len_err;
if (!header_in_data)
aeth = be32_to_cpu(ohdr->u.aeth);
else {
aeth = be32_to_cpu(((__be32 *) data)[0]);
data += sizeof(__be32);
}
- ipath_copy_sge(&qp->s_sge, data, tlen);
- if (do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST))) {
- /*
- * Change the state so we contimue
- * processing new requests and wake up the
- * tasklet if there are posted sends.
- */
- qp->s_state = OP(SEND_LAST);
- if (qp->s_tail != qp->s_head)
- tasklet_hi_schedule(&qp->s_task);
- }
+ ipath_copy_sge(&qp->s_rdma_read_sge, data, tlen);
+ (void) do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST));
goto ack_done;
}
ack_done:
spin_unlock_irqrestore(&qp->s_lock, flags);
+ goto bail;
+
+ack_op_err:
+ wc.status = IB_WC_LOC_QP_OP_ERR;
+ goto ack_err;
+
+ack_len_err:
+ wc.status = IB_WC_LOC_LEN_ERR;
+ack_err:
+ wc.wr_id = wqe->wr.wr_id;
+ wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode];
+ wc.vendor_err = 0;
+ wc.byte_len = 0;
+ wc.imm_data = 0;
+ wc.qp = &qp->ibqp;
+ wc.src_qp = qp->remote_qpn;
+ wc.wc_flags = 0;
+ wc.pkey_index = 0;
+ wc.slid = qp->remote_ah_attr.dlid;
+ wc.sl = qp->remote_ah_attr.sl;
+ wc.dlid_path_bits = 0;
+ wc.port_num = 0;
+ ipath_sqerror_qp(qp, &wc);
bail:
return;
}
@@ -1162,7 +1280,7 @@ bail:
* incoming RC packet for the given QP.
* Called at interrupt level.
* Return 1 if no more processing is needed; otherwise return 0 to
- * schedule a response to be sent and the s_lock unlocked.
+ * schedule a response to be sent.
*/
static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
struct ipath_other_headers *ohdr,
@@ -1173,25 +1291,23 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
int diff,
int header_in_data)
{
- struct ib_reth *reth;
+ struct ipath_ack_entry *e;
+ u8 i, prev;
+ int old_req;
if (diff > 0) {
/*
* Packet sequence error.
* A NAK will ACK earlier sends and RDMA writes.
- * Don't queue the NAK if a RDMA read, atomic, or
- * NAK is pending though.
+ * Don't queue the NAK if we already sent one.
*/
- if (qp->s_ack_state != OP(ACKNOWLEDGE) ||
- qp->r_nak_state != 0)
- goto done;
- if (qp->r_ack_state < OP(COMPARE_SWAP)) {
- qp->r_ack_state = OP(SEND_ONLY);
+ if (!qp->r_nak_state) {
qp->r_nak_state = IB_NAK_PSN_ERROR;
/* Use the expected PSN. */
qp->r_ack_psn = qp->r_psn;
+ goto send_ack;
}
- goto send_ack;
+ goto done;
}
/*
@@ -1204,8 +1320,46 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
* can coalesce an outstanding duplicate ACK. We have to
* send the earliest so that RDMA reads can be restarted at
* the requester's expected PSN.
+ *
+ * First, find where this duplicate PSN falls within the
+ * ACKs previously sent.
*/
- if (opcode == OP(RDMA_READ_REQUEST)) {
+ psn &= IPATH_PSN_MASK;
+ e = NULL;
+ old_req = 1;
+ spin_lock_irq(&qp->s_lock);
+ for (i = qp->r_head_ack_queue; ; i = prev) {
+ if (i == qp->s_tail_ack_queue)
+ old_req = 0;
+ if (i)
+ prev = i - 1;
+ else
+ prev = IPATH_MAX_RDMA_ATOMIC;
+ if (prev == qp->r_head_ack_queue) {
+ e = NULL;
+ break;
+ }
+ e = &qp->s_ack_queue[prev];
+ if (!e->opcode) {
+ e = NULL;
+ break;
+ }
+ if (ipath_cmp24(psn, e->psn) >= 0)
+ break;
+ }
+ switch (opcode) {
+ case OP(RDMA_READ_REQUEST): {
+ struct ib_reth *reth;
+ u32 offset;
+ u32 len;
+
+ /*
+ * If we didn't find the RDMA read request in the ack queue,
+ * or the send tasklet is already backed up to send an
+ * earlier entry, we can ignore this request.
+ */
+ if (!e || e->opcode != OP(RDMA_READ_REQUEST) || old_req)
+ goto unlock_done;
/* RETH comes after BTH */
if (!header_in_data)
reth = &ohdr->u.rc.reth;
@@ -1214,88 +1368,87 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
data += sizeof(*reth);
}
/*
- * If we receive a duplicate RDMA request, it means the
- * requester saw a sequence error and needs to restart
- * from an earlier point. We can abort the current
- * RDMA read send in that case.
+ * Address range must be a subset of the original
+ * request and start on pmtu boundaries.
+ * We reuse the old ack_queue slot since the requester
+ * should not back up and request an earlier PSN for the
+ * same request.
*/
- spin_lock_irq(&qp->s_lock);
- if (qp->s_ack_state != OP(ACKNOWLEDGE) &&
- (qp->s_hdrwords || ipath_cmp24(psn, qp->s_ack_psn) >= 0)) {
- /*
- * We are already sending earlier requested data.
- * Don't abort it to send later out of sequence data.
- */
- spin_unlock_irq(&qp->s_lock);
- goto done;
- }
- qp->s_rdma_len = be32_to_cpu(reth->length);
- if (qp->s_rdma_len != 0) {
+ offset = ((psn - e->psn) & IPATH_PSN_MASK) *
+ ib_mtu_enum_to_int(qp->path_mtu);
+ len = be32_to_cpu(reth->length);
+ if (unlikely(offset + len > e->rdma_sge.sge.sge_length))
+ goto unlock_done;
+ if (len != 0) {
u32 rkey = be32_to_cpu(reth->rkey);
u64 vaddr = be64_to_cpu(reth->vaddr);
int ok;
- /*
- * Address range must be a subset of the original
- * request and start on pmtu boundaries.
- */
- ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
- qp->s_rdma_len, vaddr, rkey,
+ ok = ipath_rkey_ok(qp, &e->rdma_sge,
+ len, vaddr, rkey,
IB_ACCESS_REMOTE_READ);
- if (unlikely(!ok)) {
- spin_unlock_irq(&qp->s_lock);
- goto done;
- }
+ if (unlikely(!ok))
+ goto unlock_done;
} else {
- qp->s_rdma_sge.sg_list = NULL;
- qp->s_rdma_sge.num_sge = 0;
- qp->s_rdma_sge.sge.mr = NULL;
- qp->s_rdma_sge.sge.vaddr = NULL;
- qp->s_rdma_sge.sge.length = 0;
- qp->s_rdma_sge.sge.sge_length = 0;
+ e->rdma_sge.sg_list = NULL;
+ e->rdma_sge.num_sge = 0;
+ e->rdma_sge.sge.mr = NULL;
+ e->rdma_sge.sge.vaddr = NULL;
+ e->rdma_sge.sge.length = 0;
+ e->rdma_sge.sge.sge_length = 0;
}
- qp->s_ack_state = opcode;
- qp->s_ack_psn = psn;
- spin_unlock_irq(&qp->s_lock);
- tasklet_hi_schedule(&qp->s_task);
- goto send_ack;
+ e->psn = psn;
+ qp->s_ack_state = OP(ACKNOWLEDGE);
+ qp->s_tail_ack_queue = prev;
+ break;
}
- /*
- * A pending RDMA read will ACK anything before it so
- * ignore earlier duplicate requests.
- */
- if (qp->s_ack_state != OP(ACKNOWLEDGE))
- goto done;
-
- /*
- * If an ACK is pending, don't replace the pending ACK
- * with an earlier one since the later one will ACK the earlier.
- * Also, if we already have a pending atomic, send it.
- */
- if (qp->r_ack_state != OP(ACKNOWLEDGE) &&
- (ipath_cmp24(psn, qp->r_ack_psn) <= 0 ||
- qp->r_ack_state >= OP(COMPARE_SWAP)))
- goto send_ack;
- switch (opcode) {
case OP(COMPARE_SWAP):
- case OP(FETCH_ADD):
+ case OP(FETCH_ADD): {
/*
- * Check for the PSN of the last atomic operation
- * performed and resend the result if found.
+ * If we didn't find the atomic request in the ack queue
+ * or the send tasklet is already backed up to send an
+ * earlier entry, we can ignore this request.
*/
- if ((psn & IPATH_PSN_MASK) != qp->r_atomic_psn)
- goto done;
+ if (!e || e->opcode != (u8) opcode || old_req)
+ goto unlock_done;
+ qp->s_ack_state = OP(ACKNOWLEDGE);
+ qp->s_tail_ack_queue = prev;
+ break;
+ }
+
+ default:
+ if (old_req)
+ goto unlock_done;
+ /*
+ * Resend the most recent ACK if this request is
+ * after all the previous RDMA reads and atomics.
+ */
+ if (i == qp->r_head_ack_queue) {
+ spin_unlock_irq(&qp->s_lock);
+ qp->r_nak_state = 0;
+ qp->r_ack_psn = qp->r_psn - 1;
+ goto send_ack;
+ }
+ /*
+ * Resend the RDMA read or atomic op which
+ * ACKs this duplicate request.
+ */
+ qp->s_ack_state = OP(ACKNOWLEDGE);
+ qp->s_tail_ack_queue = i;
break;
}
- qp->r_ack_state = opcode;
qp->r_nak_state = 0;
- qp->r_ack_psn = psn;
-send_ack:
- return 0;
+ spin_unlock_irq(&qp->s_lock);
+ tasklet_hi_schedule(&qp->s_task);
+unlock_done:
+ spin_unlock_irq(&qp->s_lock);
done:
return 1;
+
+send_ack:
+ return 0;
}
static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err)
@@ -1391,15 +1544,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
opcode == OP(SEND_LAST_WITH_IMMEDIATE))
break;
nack_inv:
- /*
- * A NAK will ACK earlier sends and RDMA writes.
- * Don't queue the NAK if a RDMA read, atomic, or NAK
- * is pending though.
- */
- if (qp->r_ack_state >= OP(COMPARE_SWAP))
- goto send_ack;
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;
goto send_ack;
@@ -1441,9 +1586,8 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
* Don't queue the NAK if a RDMA read or atomic
* is pending though.
*/
- if (qp->r_ack_state >= OP(COMPARE_SWAP))
- goto send_ack;
- qp->r_ack_state = OP(SEND_ONLY);
+ if (qp->r_nak_state)
+ goto done;
qp->r_nak_state = IB_RNR_NAK | qp->r_min_rnr_timer;
qp->r_ack_psn = qp->r_psn;
goto send_ack;
@@ -1567,7 +1711,19 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
goto rnr_nak;
goto send_last_imm;
- case OP(RDMA_READ_REQUEST):
+ case OP(RDMA_READ_REQUEST): {
+ struct ipath_ack_entry *e;
+ u32 len;
+ u8 next;
+
+ if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ)))
+ goto nack_acc;
+ next = qp->r_head_ack_queue + 1;
+ if (next > IPATH_MAX_RDMA_ATOMIC)
+ next = 0;
+ if (unlikely(next == qp->s_tail_ack_queue))
+ goto nack_inv;
+ e = &qp->s_ack_queue[qp->r_head_ack_queue];
/* RETH comes after BTH */
if (!header_in_data)
reth = &ohdr->u.rc.reth;
@@ -1575,72 +1731,75 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
reth = (struct ib_reth *)data;
data += sizeof(*reth);
}
- if (unlikely(!(qp->qp_access_flags &
- IB_ACCESS_REMOTE_READ)))
- goto nack_acc;
- spin_lock_irq(&qp->s_lock);
- qp->s_rdma_len = be32_to_cpu(reth->length);
- if (qp->s_rdma_len != 0) {
+ len = be32_to_cpu(reth->length);
+ if (len) {
u32 rkey = be32_to_cpu(reth->rkey);
u64 vaddr = be64_to_cpu(reth->vaddr);
int ok;
/* Check rkey & NAK */
- ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
- qp->s_rdma_len, vaddr, rkey,
- IB_ACCESS_REMOTE_READ);
- if (unlikely(!ok)) {
- spin_unlock_irq(&qp->s_lock);
+ ok = ipath_rkey_ok(qp, &e->rdma_sge, len, vaddr,
+ rkey, IB_ACCESS_REMOTE_READ);
+ if (unlikely(!ok))
goto nack_acc;
- }
/*
* Update the next expected PSN. We add 1 later
* below, so only add the remainder here.
*/
- if (qp->s_rdma_len > pmtu)
- qp->r_psn += (qp->s_rdma_len - 1) / pmtu;
+ if (len > pmtu)
+ qp->r_psn += (len - 1) / pmtu;
} else {
- qp->s_rdma_sge.sg_list = NULL;
- qp->s_rdma_sge.num_sge = 0;
- qp->s_rdma_sge.sge.mr = NULL;
- qp->s_rdma_sge.sge.vaddr = NULL;
- qp->s_rdma_sge.sge.length = 0;
- qp->s_rdma_sge.sge.sge_length = 0;
+ e->rdma_sge.sg_list = NULL;
+ e->rdma_sge.num_sge = 0;
+ e->rdma_sge.sge.mr = NULL;
+ e->rdma_sge.sge.vaddr = NULL;
+ e->rdma_sge.sge.length = 0;
+ e->rdma_sge.sge.sge_length = 0;
}
+ e->opcode = opcode;
+ e->psn = psn;
/*
* We need to increment the MSN here instead of when we
* finish sending the result since a duplicate request would
* increment it more than once.
*/
qp->r_msn++;
-
- qp->s_ack_state = opcode;
- qp->s_ack_psn = psn;
- spin_unlock_irq(&qp->s_lock);
-
qp->r_psn++;
qp->r_state = opcode;
qp->r_nak_state = 0;
+ barrier();
+ qp->r_head_ack_queue = next;
/* Call ipath_do_rc_send() in another thread. */
tasklet_hi_schedule(&qp->s_task);
goto done;
+ }
case OP(COMPARE_SWAP):
case OP(FETCH_ADD): {
struct ib_atomic_eth *ateth;
+ struct ipath_ack_entry *e;
u64 vaddr;
+ atomic64_t *maddr;
u64 sdata;
u32 rkey;
+ u8 next;
+ if (unlikely(!(qp->qp_access_flags &
+ IB_ACCESS_REMOTE_ATOMIC)))
+ goto nack_acc;
+ next = qp->r_head_ack_queue + 1;
+ if (next > IPATH_MAX_RDMA_ATOMIC)
+ next = 0;
+ if (unlikely(next == qp->s_tail_ack_queue))
+ goto nack_inv;
if (!header_in_data)
ateth = &ohdr->u.atomic_eth;
- else {
+ else
ateth = (struct ib_atomic_eth *)data;
- data += sizeof(*ateth);
- }
- vaddr = be64_to_cpu(ateth->vaddr);
+ vaddr = ((u64) be32_to_cpu(ateth->vaddr[0]) << 32) |
+ be32_to_cpu(ateth->vaddr[1]);
if (unlikely(vaddr & (sizeof(u64) - 1)))
goto nack_inv;
rkey = be32_to_cpu(ateth->rkey);
@@ -1649,63 +1808,50 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
sizeof(u64), vaddr, rkey,
IB_ACCESS_REMOTE_ATOMIC)))
goto nack_acc;
- if (unlikely(!(qp->qp_access_flags &
- IB_ACCESS_REMOTE_ATOMIC)))
- goto nack_acc;
/* Perform atomic OP and save result. */
+ maddr = (atomic64_t *) qp->r_sge.sge.vaddr;
sdata = be64_to_cpu(ateth->swap_data);
- spin_lock_irq(&dev->pending_lock);
- qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr;
- if (opcode == OP(FETCH_ADD))
- *(u64 *) qp->r_sge.sge.vaddr =
- qp->r_atomic_data + sdata;
- else if (qp->r_atomic_data ==
- be64_to_cpu(ateth->compare_data))
- *(u64 *) qp->r_sge.sge.vaddr = sdata;
- spin_unlock_irq(&dev->pending_lock);
+ e = &qp->s_ack_queue[qp->r_head_ack_queue];
+ e->atomic_data = (opcode == OP(FETCH_ADD)) ?
+ (u64) atomic64_add_return(sdata, maddr) - sdata :
+ (u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr,
+ be64_to_cpu(ateth->compare_data),
+ sdata);
+ e->opcode = opcode;
+ e->psn = psn & IPATH_PSN_MASK;
qp->r_msn++;
- qp->r_atomic_psn = psn & IPATH_PSN_MASK;
- psn |= 1 << 31;
- break;
+ qp->r_psn++;
+ qp->r_state = opcode;
+ qp->r_nak_state = 0;
+ barrier();
+ qp->r_head_ack_queue = next;
+
+ /* Call ipath_do_rc_send() in another thread. */
+ tasklet_hi_schedule(&qp->s_task);
+
+ goto done;
}
default:
- /* Drop packet for unknown opcodes. */
- goto done;
+ /* NAK unknown opcodes. */
+ goto nack_inv;
}
qp->r_psn++;
qp->r_state = opcode;
+ qp->r_ack_psn = psn;
qp->r_nak_state = 0;
/* Send an ACK if requested or required. */
- if (psn & (1 << 31)) {
- /*
- * Coalesce ACKs unless there is a RDMA READ or
- * ATOMIC pending.
- */
- if (qp->r_ack_state < OP(COMPARE_SWAP)) {
- qp->r_ack_state = opcode;
- qp->r_ack_psn = psn;
- }
+ if (psn & (1 << 31))
goto send_ack;
- }
goto done;
nack_acc:
- /*
- * A NAK will ACK earlier sends and RDMA writes.
- * Don't queue the NAK if a RDMA read, atomic, or NAK
- * is pending though.
- */
- if (qp->r_ack_state < OP(COMPARE_SWAP)) {
- 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;
- }
+ ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR);
+ qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
+ qp->r_ack_psn = qp->r_psn;
+
send_ack:
- /* Send ACK right away unless the send tasklet has a pending ACK. */
- if (qp->s_ack_state == OP(ACKNOWLEDGE))
- send_rc_ack(qp);
+ send_rc_ack(qp);
done:
return;
diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h
index dffc76016d3c..c182bcd62098 100644
--- a/drivers/infiniband/hw/ipath/ipath_registers.h
+++ b/drivers/infiniband/hw/ipath/ipath_registers.h
@@ -126,9 +126,18 @@
#define INFINIPATH_E_RESET 0x0004000000000000ULL
#define INFINIPATH_E_HARDWARE 0x0008000000000000ULL
+/*
+ * this is used to print "common" packet errors only when the
+ * __IPATH_ERRPKTDBG bit is set in ipath_debug.
+ */
+#define INFINIPATH_E_PKTERRS ( INFINIPATH_E_SPKTLEN \
+ | INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_RVCRC \
+ | INFINIPATH_E_RICRC | INFINIPATH_E_RSHORTPKTLEN \
+ | INFINIPATH_E_REBP )
+
/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
/* TXEMEMPARITYERR bit 0: PIObuf, 1: PIOpbc, 2: launchfifo
- * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: eagerTID, 3: expTID
+ * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: expTID, 3: eagerTID
* bit 4: flag buffer, 5: datainfo, 6: header info */
#define INFINIPATH_HWE_TXEMEMPARITYERR_MASK 0xFULL
#define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40
@@ -143,8 +152,8 @@
/* 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_EXPTID 0x04ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x08ULL
#define INFINIPATH_HWE_RXEMEMPARITYERR_FLAGBUF 0x10ULL
#define INFINIPATH_HWE_RXEMEMPARITYERR_DATAINFO 0x20ULL
#define INFINIPATH_HWE_RXEMEMPARITYERR_HDRINFO 0x40ULL
@@ -299,13 +308,6 @@
#define INFINIPATH_XGXS_RX_POL_SHIFT 19
#define INFINIPATH_XGXS_RX_POL_MASK 0xfULL
-#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL /* 40 bits valid */
-
-/* TID entries (memory), HT-only */
-#define INFINIPATH_RT_VALID 0x8000000000000000ULL
-#define INFINIPATH_RT_ADDR_SHIFT 0
-#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFF
-#define INFINIPATH_RT_BUFSIZE_SHIFT 48
/*
* IPATH_PIO_MAXIBHDR is the max IB header size allowed for in our
diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c
index e86cb171872e..d9c2a9b15d86 100644
--- a/drivers/infiniband/hw/ipath/ipath_ruc.c
+++ b/drivers/infiniband/hw/ipath/ipath_ruc.c
@@ -202,6 +202,7 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only)
wq->tail = tail;
ret = 1;
+ qp->r_wrid_valid = 1;
if (handler) {
u32 n;
@@ -229,7 +230,6 @@ 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;
@@ -255,6 +255,7 @@ static void ipath_ruc_loopback(struct ipath_qp *sqp)
unsigned long flags;
struct ib_wc wc;
u64 sdata;
+ atomic64_t *maddr;
qp = ipath_lookup_qpn(&dev->qp_table, sqp->remote_qpn);
if (!qp) {
@@ -265,7 +266,8 @@ static void ipath_ruc_loopback(struct ipath_qp *sqp)
again:
spin_lock_irqsave(&sqp->s_lock, flags);
- if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK)) {
+ if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK) ||
+ qp->s_rnr_timeout) {
spin_unlock_irqrestore(&sqp->s_lock, flags);
goto done;
}
@@ -310,7 +312,7 @@ again:
sqp->s_rnr_retry--;
dev->n_rnr_naks++;
sqp->s_rnr_timeout =
- ib_ipath_rnr_table[sqp->r_min_rnr_timer];
+ ib_ipath_rnr_table[qp->r_min_rnr_timer];
ipath_insert_rnr_queue(sqp);
goto done;
}
@@ -343,20 +345,22 @@ again:
wc.sl = sqp->remote_ah_attr.sl;
wc.dlid_path_bits = 0;
wc.port_num = 0;
+ spin_lock_irqsave(&sqp->s_lock, flags);
ipath_sqerror_qp(sqp, &wc);
+ spin_unlock_irqrestore(&sqp->s_lock, flags);
goto done;
}
break;
case IB_WR_RDMA_READ:
+ if (unlikely(!(qp->qp_access_flags &
+ IB_ACCESS_REMOTE_READ)))
+ goto acc_err;
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)))
goto acc_err;
- if (unlikely(!(qp->qp_access_flags &
- IB_ACCESS_REMOTE_READ)))
- goto acc_err;
qp->r_sge.sge = wqe->sg_list[0];
qp->r_sge.sg_list = wqe->sg_list + 1;
qp->r_sge.num_sge = wqe->wr.num_sge;
@@ -364,22 +368,22 @@ again:
case IB_WR_ATOMIC_CMP_AND_SWP:
case IB_WR_ATOMIC_FETCH_AND_ADD:
+ if (unlikely(!(qp->qp_access_flags &
+ IB_ACCESS_REMOTE_ATOMIC)))
+ goto acc_err;
if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64),
- wqe->wr.wr.rdma.remote_addr,
- wqe->wr.wr.rdma.rkey,
+ wqe->wr.wr.atomic.remote_addr,
+ wqe->wr.wr.atomic.rkey,
IB_ACCESS_REMOTE_ATOMIC)))
goto acc_err;
/* Perform atomic OP and save result. */
- sdata = wqe->wr.wr.atomic.swap;
- spin_lock_irqsave(&dev->pending_lock, flags);
- qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr;
- if (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)
- *(u64 *) qp->r_sge.sge.vaddr =
- qp->r_atomic_data + sdata;
- else if (qp->r_atomic_data == wqe->wr.wr.atomic.compare_add)
- *(u64 *) qp->r_sge.sge.vaddr = sdata;
- spin_unlock_irqrestore(&dev->pending_lock, flags);
- *(u64 *) sqp->s_sge.sge.vaddr = qp->r_atomic_data;
+ maddr = (atomic64_t *) qp->r_sge.sge.vaddr;
+ sdata = wqe->wr.wr.atomic.compare_add;
+ *(u64 *) sqp->s_sge.sge.vaddr =
+ (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) ?
+ (u64) atomic64_add_return(sdata, maddr) - sdata :
+ (u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr,
+ sdata, wqe->wr.wr.atomic.swap);
goto send_comp;
default:
@@ -440,7 +444,7 @@ again:
send_comp:
sqp->s_rnr_retry = sqp->s_rnr_retry_cnt;
- if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &sqp->s_flags) ||
+ if (!(sqp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
(wqe->wr.send_flags & IB_SEND_SIGNALED)) {
wc.wr_id = wqe->wr.wr_id;
wc.status = IB_WC_SUCCESS;
@@ -502,7 +506,7 @@ void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev)
* We clear the tasklet flag now since we are committing to return
* from the tasklet function.
*/
- clear_bit(IPATH_S_BUSY, &qp->s_flags);
+ clear_bit(IPATH_S_BUSY, &qp->s_busy);
tasklet_unlock(&qp->s_task);
want_buffer(dev->dd);
dev->n_piowait++;
@@ -541,6 +545,9 @@ int ipath_post_ruc_send(struct ipath_qp *qp, struct ib_send_wr *wr)
wr->sg_list[0].addr & (sizeof(u64) - 1))) {
ret = -EINVAL;
goto bail;
+ } else if (wr->opcode >= IB_WR_RDMA_READ && !qp->s_max_rd_atomic) {
+ ret = -EINVAL;
+ goto bail;
}
/* IB spec says that num_sge == 0 is OK. */
if (wr->num_sge > qp->s_max_sge) {
@@ -647,7 +654,7 @@ void ipath_do_ruc_send(unsigned long data)
u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
struct ipath_other_headers *ohdr;
- if (test_and_set_bit(IPATH_S_BUSY, &qp->s_flags))
+ if (test_and_set_bit(IPATH_S_BUSY, &qp->s_busy))
goto bail;
if (unlikely(qp->remote_ah_attr.dlid == dev->dd->ipath_lid)) {
@@ -683,19 +690,15 @@ again:
*/
spin_lock_irqsave(&qp->s_lock, flags);
- /* Sending responses has higher priority over sending requests. */
- if (qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE &&
- (bth0 = ipath_make_rc_ack(qp, ohdr, pmtu)) != 0)
- bth2 = qp->s_ack_psn++ & IPATH_PSN_MASK;
- else if (!((qp->ibqp.qp_type == IB_QPT_RC) ?
- ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2) :
- ipath_make_uc_req(qp, ohdr, pmtu, &bth0, &bth2))) {
+ if (!((qp->ibqp.qp_type == IB_QPT_RC) ?
+ ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2) :
+ ipath_make_uc_req(qp, ohdr, pmtu, &bth0, &bth2))) {
/*
* Clear the busy bit before unlocking to avoid races with
* adding new work queue items and then failing to process
* them.
*/
- clear_bit(IPATH_S_BUSY, &qp->s_flags);
+ clear_bit(IPATH_S_BUSY, &qp->s_busy);
spin_unlock_irqrestore(&qp->s_lock, flags);
goto bail;
}
@@ -728,7 +731,7 @@ again:
goto again;
clear:
- clear_bit(IPATH_S_BUSY, &qp->s_flags);
+ clear_bit(IPATH_S_BUSY, &qp->s_busy);
bail:
return;
}
diff --git a/drivers/infiniband/hw/ipath/ipath_stats.c b/drivers/infiniband/hw/ipath/ipath_stats.c
index 30a825928fcf..9307f7187ca5 100644
--- a/drivers/infiniband/hw/ipath/ipath_stats.c
+++ b/drivers/infiniband/hw/ipath/ipath_stats.c
@@ -207,7 +207,7 @@ void ipath_get_faststats(unsigned long opaque)
* don't access the chip while running diags, or memory diags can
* fail
*/
- if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) ||
+ if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_INITTED) ||
ipath_diag_inuse)
/* but re-arm the timer, for diags case; won't hurt other */
goto done;
@@ -237,11 +237,13 @@ void ipath_get_faststats(unsigned long opaque)
if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs)
&& time_after(jiffies, dd->ipath_unmasktime)) {
char ebuf[256];
- ipath_decode_err(ebuf, sizeof ebuf,
+ int iserr;
+ iserr = ipath_decode_err(ebuf, sizeof ebuf,
(dd->ipath_maskederrs & ~dd->
ipath_ignorederrs));
if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) &
- ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL))
+ ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL |
+ INFINIPATH_E_PKTERRS ))
ipath_dev_err(dd, "Re-enabling masked errors "
"(%s)\n", ebuf);
else {
@@ -252,8 +254,12 @@ void ipath_get_faststats(unsigned long opaque)
* them. So only complain about these at debug
* level.
*/
- ipath_dbg("Disabling frequent queue full errors "
- "(%s)\n", ebuf);
+ if (iserr)
+ ipath_dbg("Re-enabling queue full errors (%s)\n",
+ ebuf);
+ else
+ ipath_cdbg(ERRPKT, "Re-enabling packet"
+ " problem interrupt (%s)\n", ebuf);
}
dd->ipath_maskederrs = dd->ipath_ignorederrs;
ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask,
diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c
index 325d6634ff53..1c2b03c2ef5e 100644
--- a/drivers/infiniband/hw/ipath/ipath_uc.c
+++ b/drivers/infiniband/hw/ipath/ipath_uc.c
@@ -42,7 +42,7 @@ static void complete_last_send(struct ipath_qp *qp, struct ipath_swqe *wqe,
{
if (++qp->s_last == qp->s_size)
qp->s_last = 0;
- if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
+ if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
(wqe->wr.send_flags & IB_SEND_SIGNALED)) {
wc->wr_id = wqe->wr.wr_id;
wc->status = IB_WC_SUCCESS;
@@ -344,13 +344,13 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
send_first:
if (qp->r_reuse_sge) {
qp->r_reuse_sge = 0;
- qp->r_sge = qp->s_rdma_sge;
+ qp->r_sge = qp->s_rdma_read_sge;
} else if (!ipath_get_rwqe(qp, 0)) {
dev->n_pkt_drops++;
goto done;
}
/* Save the WQE so we can reuse it in case of an error. */
- qp->s_rdma_sge = qp->r_sge;
+ qp->s_rdma_read_sge = qp->r_sge;
qp->r_rcv_len = 0;
if (opcode == OP(SEND_ONLY))
goto send_last;
diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c
index 9a3e54664ee4..a518f7c8fa83 100644
--- a/drivers/infiniband/hw/ipath/ipath_ud.c
+++ b/drivers/infiniband/hw/ipath/ipath_ud.c
@@ -308,6 +308,11 @@ int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr)
goto bail;
}
+ if (wr->wr.ud.ah->pd != qp->ibqp.pd) {
+ ret = -EPERM;
+ goto bail;
+ }
+
/* IB spec says that num_sge == 0 is OK. */
if (wr->num_sge > qp->s_max_sge) {
ret = -EINVAL;
@@ -467,7 +472,7 @@ int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr)
done:
/* Queue the completion status entry. */
- if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
+ if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
(wr->send_flags & IB_SEND_SIGNALED)) {
wc.wr_id = wr->wr_id;
wc.status = IB_WC_SUCCESS;
@@ -647,6 +652,7 @@ void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
ipath_skip_sge(&qp->r_sge, sizeof(struct ib_grh));
ipath_copy_sge(&qp->r_sge, data,
wc.byte_len - sizeof(struct ib_grh));
+ qp->r_wrid_valid = 0;
wc.wr_id = qp->r_wr_id;
wc.status = IB_WC_SUCCESS;
wc.opcode = IB_WC_RECV;
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index 2aaacdb7e52a..18c6df2052c2 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -438,6 +438,10 @@ void ipath_ib_rcv(struct ipath_ibdev *dev, void *rhdr, void *data,
struct ipath_mcast *mcast;
struct ipath_mcast_qp *p;
+ if (lnh != IPATH_LRH_GRH) {
+ dev->n_pkt_drops++;
+ goto bail;
+ }
mcast = ipath_mcast_find(&hdr->u.l.grh.dgid);
if (mcast == NULL) {
dev->n_pkt_drops++;
@@ -445,8 +449,7 @@ void ipath_ib_rcv(struct ipath_ibdev *dev, void *rhdr, void *data,
}
dev->n_multicast_rcv++;
list_for_each_entry_rcu(p, &mcast->qp_list, list)
- ipath_qp_rcv(dev, hdr, lnh == IPATH_LRH_GRH, data,
- tlen, p->qp);
+ ipath_qp_rcv(dev, hdr, 1, data, tlen, p->qp);
/*
* Notify ipath_multicast_detach() if it is waiting for us
* to finish.
@@ -773,7 +776,6 @@ int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords,
/* +1 is for the qword padding of pbc */
plen = hdrwords + ((len + 3) >> 2) + 1;
if (unlikely((plen << 2) > dd->ipath_ibmaxlen)) {
- ipath_dbg("packet len 0x%x too long, failing\n", plen);
ret = -EINVAL;
goto bail;
}
@@ -980,14 +982,14 @@ static int ipath_query_device(struct ib_device *ibdev,
props->max_cqe = ib_ipath_max_cqes;
props->max_mr = dev->lk_table.max;
props->max_pd = ib_ipath_max_pds;
- props->max_qp_rd_atom = 1;
- props->max_qp_init_rd_atom = 1;
+ props->max_qp_rd_atom = IPATH_MAX_RDMA_ATOMIC;
+ props->max_qp_init_rd_atom = 255;
/* props->max_res_rd_atom */
props->max_srq = ib_ipath_max_srqs;
props->max_srq_wr = ib_ipath_max_srq_wrs;
props->max_srq_sge = ib_ipath_max_srq_sges;
/* props->local_ca_ack_delay */
- props->atomic_cap = IB_ATOMIC_HCA;
+ props->atomic_cap = IB_ATOMIC_GLOB;
props->max_pkeys = ipath_get_npkeys(dev->dd);
props->max_mcast_grp = ib_ipath_max_mcast_grps;
props->max_mcast_qp_attach = ib_ipath_max_mcast_qp_attached;
@@ -1557,7 +1559,6 @@ int ipath_register_ib_device(struct ipath_devdata *dd)
dev->node_type = RDMA_NODE_IB_CA;
dev->phys_port_cnt = 1;
dev->dma_device = &dd->pcidev->dev;
- dev->class_dev.dev = dev->dma_device;
dev->query_device = ipath_query_device;
dev->modify_device = ipath_modify_device;
dev->query_port = ipath_query_port;
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h
index c0c8d5b24a7d..7c4929f1cb5b 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.h
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.h
@@ -40,9 +40,12 @@
#include <linux/interrupt.h>
#include <linux/kref.h>
#include <rdma/ib_pack.h>
+#include <rdma/ib_user_verbs.h>
#include "ipath_layer.h"
+#define IPATH_MAX_RDMA_ATOMIC 4
+
#define QPN_MAX (1 << 24)
#define QPNMAP_ENTRIES (QPN_MAX / PAGE_SIZE / BITS_PER_BYTE)
@@ -89,7 +92,7 @@ struct ib_reth {
} __attribute__ ((packed));
struct ib_atomic_eth {
- __be64 vaddr;
+ __be32 vaddr[2]; /* unaligned so access as 2 32-bit words */
__be32 rkey;
__be64 swap_data;
__be64 compare_data;
@@ -108,7 +111,7 @@ struct ipath_other_headers {
} rc;
struct {
__be32 aeth;
- __be64 atomic_ack_eth;
+ __be32 atomic_ack_eth[2];
} at;
__be32 imm_data;
__be32 aeth;
@@ -186,7 +189,7 @@ struct ipath_mmap_info {
struct ipath_cq_wc {
u32 head; /* index of next entry to fill */
u32 tail; /* index of next ib_poll_cq() entry */
- struct ib_wc queue[1]; /* this is actually size ibcq.cqe + 1 */
+ struct ib_uverbs_wc queue[1]; /* this is actually size ibcq.cqe + 1 */
};
/*
@@ -312,6 +315,19 @@ struct ipath_sge_state {
};
/*
+ * This structure holds the information that the send tasklet needs
+ * to send a RDMA read response or atomic operation.
+ */
+struct ipath_ack_entry {
+ u8 opcode;
+ u32 psn;
+ union {
+ struct ipath_sge_state rdma_sge;
+ u64 atomic_data;
+ };
+};
+
+/*
* Variables prefixed with s_ are for the requester (sender).
* Variables prefixed with r_ are for the responder (receiver).
* Variables prefixed with ack_ are for responder replies.
@@ -333,24 +349,24 @@ struct ipath_qp {
struct ipath_mmap_info *ip;
struct ipath_sge_state *s_cur_sge;
struct ipath_sge_state s_sge; /* current send request data */
- /* current RDMA read send data */
- struct ipath_sge_state s_rdma_sge;
+ struct ipath_ack_entry s_ack_queue[IPATH_MAX_RDMA_ATOMIC + 1];
+ struct ipath_sge_state s_ack_rdma_sge;
+ struct ipath_sge_state s_rdma_read_sge;
struct ipath_sge_state r_sge; /* current receive data */
spinlock_t s_lock;
- unsigned long s_flags;
+ unsigned long s_busy;
u32 s_hdrwords; /* size of s_hdr in 32 bit words */
u32 s_cur_size; /* size of send packet in bytes */
u32 s_len; /* total length of s_sge */
- u32 s_rdma_len; /* total length of s_rdma_sge */
+ u32 s_rdma_read_len; /* total length of s_rdma_read_sge */
u32 s_next_psn; /* PSN for next request */
u32 s_last_psn; /* last response PSN processed */
u32 s_psn; /* current packet sequence number */
- u32 s_ack_psn; /* PSN for RDMA_READ */
+ u32 s_ack_rdma_psn; /* PSN for sending RDMA read responses */
+ u32 s_ack_psn; /* PSN for acking sends and RDMA writes */
u32 s_rnr_timeout; /* number of milliseconds for RNR timeout */
u32 r_ack_psn; /* PSN for next ACK or atomic ACK */
u64 r_wr_id; /* ID for current receive WQE */
- u64 r_atomic_data; /* data for last atomic op */
- u32 r_atomic_psn; /* PSN of last atomic op */
u32 r_len; /* total length of r_sge */
u32 r_rcv_len; /* receive data len processed */
u32 r_psn; /* expected rcv packet sequence number */
@@ -360,12 +376,13 @@ struct ipath_qp {
u8 s_ack_state; /* opcode of packet to ACK */
u8 s_nak_state; /* non-zero if NAK is pending */
u8 r_state; /* opcode of last packet received */
- u8 r_ack_state; /* opcode of packet to ACK */
u8 r_nak_state; /* non-zero if NAK is pending */
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 r_max_rd_atomic; /* max number of RDMA read/atomic to receive */
+ u8 r_head_ack_queue; /* index into s_ack_queue[] */
u8 qp_access_flags;
u8 s_max_sge; /* size of s_wq->sg_list */
u8 s_retry_cnt; /* number of times to retry */
@@ -374,6 +391,10 @@ struct ipath_qp {
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 s_max_rd_atomic; /* max number of RDMA read/atomic to send */
+ u8 s_num_rd_atomic; /* number of RDMA read/atomic pending */
+ u8 s_tail_ack_queue; /* index into s_ack_queue[] */
+ u8 s_flags;
u8 timeout; /* Timeout for this QP */
enum ib_mtu path_mtu;
u32 remote_qpn;
@@ -390,11 +411,16 @@ struct ipath_qp {
struct ipath_sge r_sg_list[0]; /* verified SGEs */
};
+/* Bit definition for s_busy. */
+#define IPATH_S_BUSY 0
+
/*
* Bit definitions for s_flags.
*/
-#define IPATH_S_BUSY 0
-#define IPATH_S_SIGNAL_REQ_WR 1
+#define IPATH_S_SIGNAL_REQ_WR 0x01
+#define IPATH_S_FENCE_PENDING 0x02
+#define IPATH_S_RDMAR_PENDING 0x04
+#define IPATH_S_ACK_PENDING 0x08
#define IPATH_PSN_CREDIT 2048
@@ -706,8 +732,6 @@ int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr);
int ipath_destroy_srq(struct ib_srq *ibsrq);
-void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
-
int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
@@ -757,9 +781,6 @@ u32 ipath_make_grh(struct ipath_ibdev *dev, struct ib_grh *hdr,
void ipath_do_ruc_send(unsigned long data);
-u32 ipath_make_rc_ack(struct ipath_qp *qp, struct ipath_other_headers *ohdr,
- u32 pmtu);
-
int ipath_make_rc_req(struct ipath_qp *qp, struct ipath_other_headers *ohdr,
u32 pmtu, u32 *bth0p, u32 *bth2p);
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
index 0d9b7d06bbc2..773145e29947 100644
--- a/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/drivers/infiniband/hw/mthca/mthca_main.c
@@ -1013,14 +1013,14 @@ static struct {
u64 latest_fw;
u32 flags;
} mthca_hca_table[] = {
- [TAVOR] = { .latest_fw = MTHCA_FW_VER(3, 4, 0),
+ [TAVOR] = { .latest_fw = MTHCA_FW_VER(3, 5, 0),
.flags = 0 },
- [ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 7, 600),
+ [ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 8, 200),
.flags = MTHCA_FLAG_PCIE },
- [ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 1, 400),
+ [ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 2, 0),
.flags = MTHCA_FLAG_MEMFREE |
MTHCA_FLAG_PCIE },
- [SINAI] = { .latest_fw = MTHCA_FW_VER(1, 1, 0),
+ [SINAI] = { .latest_fw = MTHCA_FW_VER(1, 2, 0),
.flags = MTHCA_FLAG_MEMFREE |
MTHCA_FLAG_PCIE |
MTHCA_FLAG_SINAI_OPT }
@@ -1135,7 +1135,7 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type)
goto err_cmd;
if (mdev->fw_ver < mthca_hca_table[hca_type].latest_fw) {
- mthca_warn(mdev, "HCA FW version %d.%d.%d is old (%d.%d.%d is current).\n",
+ mthca_warn(mdev, "HCA FW version %d.%d.%3d is old (%d.%d.%3d is current).\n",
(int) (mdev->fw_ver >> 32), (int) (mdev->fw_ver >> 16) & 0xffff,
(int) (mdev->fw_ver & 0xffff),
(int) (mthca_hca_table[hca_type].latest_fw >> 32),
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
index fdb576dcfaa8..aa6c70a6a36f 100644
--- a/drivers/infiniband/hw/mthca/mthca_mr.c
+++ b/drivers/infiniband/hw/mthca/mthca_mr.c
@@ -297,7 +297,8 @@ out:
int mthca_write_mtt_size(struct mthca_dev *dev)
{
- if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy)
+ if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy ||
+ !(dev->mthca_flags & MTHCA_FLAG_FMR))
/*
* Be friendly to WRITE_MTT command
* and leave two empty slots for the
@@ -355,7 +356,8 @@ int mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt,
int size = mthca_write_mtt_size(dev);
int chunk;
- if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy)
+ if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy ||
+ !(dev->mthca_flags & MTHCA_FLAG_FMR))
return __mthca_write_mtt(dev, mtt, start_index, buffer_list, list_len);
while (list_len > 0) {
@@ -835,6 +837,7 @@ void mthca_arbel_fmr_unmap(struct mthca_dev *dev, struct mthca_fmr *fmr)
key = arbel_key_to_hw_index(fmr->ibmr.lkey);
key &= dev->limits.num_mpts - 1;
+ key = adjust_key(dev, key);
fmr->ibmr.lkey = fmr->ibmr.rkey = arbel_hw_index_to_key(key);
fmr->maps = 0;
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index 0725ad7ad9bf..47e6fd46d9c2 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -1293,7 +1293,6 @@ int mthca_register_device(struct mthca_dev *dev)
dev->ib_dev.node_type = RDMA_NODE_IB_CA;
dev->ib_dev.phys_port_cnt = dev->limits.num_ports;
dev->ib_dev.dma_device = &dev->pdev->dev;
- dev->ib_dev.class_dev.dev = &dev->pdev->dev;
dev->ib_dev.query_device = mthca_query_device;
dev->ib_dev.query_port = mthca_query_port;
dev->ib_dev.modify_device = mthca_modify_device;
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c
index 1c6b63aca268..8fe6fee7a97a 100644
--- a/drivers/infiniband/hw/mthca/mthca_qp.c
+++ b/drivers/infiniband/hw/mthca/mthca_qp.c
@@ -1419,11 +1419,10 @@ void mthca_free_qp(struct mthca_dev *dev,
* unref the mem-free tables and free the QPN in our table.
*/
if (!qp->ibqp.uobject) {
- mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq), qp->qpn,
+ mthca_cq_clean(dev, recv_cq, qp->qpn,
qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL);
- if (qp->ibqp.send_cq != qp->ibqp.recv_cq)
- mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn,
- qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL);
+ if (send_cq != recv_cq)
+ mthca_cq_clean(dev, send_cq, qp->qpn, NULL);
mthca_free_memfree(dev, qp);
mthca_free_wqe_buf(dev, qp);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 2b242a4823f8..0c4e59b906cd 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -228,7 +228,6 @@ static int ipoib_cm_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even
struct net_device *dev = cm_id->context;
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_cm_rx *p;
- unsigned long flags;
unsigned psn;
int ret;
@@ -257,9 +256,9 @@ static int ipoib_cm_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even
cm_id->context = p;
p->jiffies = jiffies;
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
list_add(&p->list, &priv->cm.passive_ids);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
queue_delayed_work(ipoib_workqueue,
&priv->cm.stale_task, IPOIB_CM_RX_DELAY);
return 0;
@@ -277,7 +276,6 @@ static int ipoib_cm_rx_handler(struct ib_cm_id *cm_id,
{
struct ipoib_cm_rx *p;
struct ipoib_dev_priv *priv;
- unsigned long flags;
int ret;
switch (event->event) {
@@ -290,14 +288,14 @@ static int ipoib_cm_rx_handler(struct ib_cm_id *cm_id,
case IB_CM_REJ_RECEIVED:
p = cm_id->context;
priv = netdev_priv(p->dev);
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
if (list_empty(&p->list))
ret = 0; /* Connection is going away already. */
else {
list_del_init(&p->list);
ret = -ECONNRESET;
}
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
if (ret) {
ib_destroy_qp(p->qp);
kfree(p);
@@ -351,8 +349,8 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
u64 mapping[IPOIB_CM_RX_SG];
int frags;
- ipoib_dbg_data(priv, "cm recv completion: id %d, op %d, status: %d\n",
- wr_id, wc->opcode, wc->status);
+ ipoib_dbg_data(priv, "cm recv completion: id %d, status: %d\n",
+ wr_id, wc->status);
if (unlikely(wr_id >= ipoib_recvq_size)) {
ipoib_warn(priv, "cm recv completion event with wrid %d (> %d)\n",
@@ -408,7 +406,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
skb_put_frags(skb, IPOIB_CM_HEAD_SIZE, wc->byte_len, newskb);
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, IPOIB_ENCAP_LEN);
dev->last_rx = jiffies;
@@ -504,8 +502,8 @@ static void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ipoib_cm_tx *tx
struct ipoib_tx_buf *tx_req;
unsigned long flags;
- ipoib_dbg_data(priv, "cm send completion: id %d, op %d, status: %d\n",
- wr_id, wc->opcode, wc->status);
+ ipoib_dbg_data(priv, "cm send completion: id %d, status: %d\n",
+ wr_id, wc->status);
if (unlikely(wr_id >= ipoib_sendq_size)) {
ipoib_warn(priv, "cm send completion event with wrid %d (> %d)\n",
@@ -612,23 +610,22 @@ void ipoib_cm_dev_stop(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_cm_rx *p;
- unsigned long flags;
if (!IPOIB_CM_SUPPORTED(dev->dev_addr))
return;
ib_destroy_cm_id(priv->cm.id);
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
while (!list_empty(&priv->cm.passive_ids)) {
p = list_entry(priv->cm.passive_ids.next, typeof(*p), list);
list_del_init(&p->list);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
ib_destroy_cm_id(p->id);
ib_destroy_qp(p->qp);
kfree(p);
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
}
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
cancel_delayed_work(&priv->cm.stale_task);
}
@@ -642,7 +639,6 @@ static int ipoib_cm_rep_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even
struct ib_qp_attr qp_attr;
int qp_attr_mask, ret;
struct sk_buff *skb;
- unsigned long flags;
p->mtu = be32_to_cpu(data->mtu);
@@ -680,12 +676,12 @@ static int ipoib_cm_rep_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even
skb_queue_head_init(&skqueue);
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
set_bit(IPOIB_FLAG_OPER_UP, &p->flags);
if (p->neigh)
while ((skb = __skb_dequeue(&p->neigh->queue)))
__skb_queue_tail(&skqueue, skb);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
while ((skb = __skb_dequeue(&skqueue))) {
skb->dev = p->dev;
@@ -895,7 +891,6 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id,
struct ipoib_dev_priv *priv = netdev_priv(tx->dev);
struct net_device *dev = priv->dev;
struct ipoib_neigh *neigh;
- unsigned long flags;
int ret;
switch (event->event) {
@@ -914,7 +909,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id,
case IB_CM_REJ_RECEIVED:
case IB_CM_TIMEWAIT_EXIT:
ipoib_dbg(priv, "CM error %d.\n", event->event);
- spin_lock_irqsave(&priv->tx_lock, flags);
+ spin_lock_irq(&priv->tx_lock);
spin_lock(&priv->lock);
neigh = tx->neigh;
@@ -934,7 +929,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id,
}
spin_unlock(&priv->lock);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
+ spin_unlock_irq(&priv->tx_lock);
break;
default:
break;
@@ -1023,21 +1018,20 @@ static void ipoib_cm_tx_reap(struct work_struct *work)
struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv,
cm.reap_task);
struct ipoib_cm_tx *p;
- unsigned long flags;
- spin_lock_irqsave(&priv->tx_lock, flags);
+ spin_lock_irq(&priv->tx_lock);
spin_lock(&priv->lock);
while (!list_empty(&priv->cm.reap_list)) {
p = list_entry(priv->cm.reap_list.next, typeof(*p), list);
list_del(&p->list);
spin_unlock(&priv->lock);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
+ spin_unlock_irq(&priv->tx_lock);
ipoib_cm_tx_destroy(p);
- spin_lock_irqsave(&priv->tx_lock, flags);
+ spin_lock_irq(&priv->tx_lock);
spin_lock(&priv->lock);
}
spin_unlock(&priv->lock);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
+ spin_unlock_irq(&priv->tx_lock);
}
static void ipoib_cm_skb_reap(struct work_struct *work)
@@ -1046,15 +1040,14 @@ static void ipoib_cm_skb_reap(struct work_struct *work)
cm.skb_task);
struct net_device *dev = priv->dev;
struct sk_buff *skb;
- unsigned long flags;
unsigned mtu = priv->mcast_mtu;
- spin_lock_irqsave(&priv->tx_lock, flags);
+ spin_lock_irq(&priv->tx_lock);
spin_lock(&priv->lock);
while ((skb = skb_dequeue(&priv->cm.skb_queue))) {
spin_unlock(&priv->lock);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
+ spin_unlock_irq(&priv->tx_lock);
if (skb->protocol == htons(ETH_P_IP))
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -1062,11 +1055,11 @@ static void ipoib_cm_skb_reap(struct work_struct *work)
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
#endif
dev_kfree_skb_any(skb);
- spin_lock_irqsave(&priv->tx_lock, flags);
+ spin_lock_irq(&priv->tx_lock);
spin_lock(&priv->lock);
}
spin_unlock(&priv->lock);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
+ spin_unlock_irq(&priv->tx_lock);
}
void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb,
@@ -1088,9 +1081,8 @@ static void ipoib_cm_stale_task(struct work_struct *work)
struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv,
cm.stale_task.work);
struct ipoib_cm_rx *p;
- unsigned long flags;
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
while (!list_empty(&priv->cm.passive_ids)) {
/* List if sorted by LRU, start from tail,
* stop when we see a recently used entry */
@@ -1098,13 +1090,13 @@ static void ipoib_cm_stale_task(struct work_struct *work)
if (time_before_eq(jiffies, p->jiffies + IPOIB_CM_RX_TIMEOUT))
break;
list_del_init(&p->list);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
ib_destroy_cm_id(p->id);
ib_destroy_qp(p->qp);
kfree(p);
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock);
}
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irq(&priv->lock);
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index ba0ee5cf2ad7..1bdb9101911a 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -172,8 +172,8 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
struct sk_buff *skb;
u64 addr;
- ipoib_dbg_data(priv, "recv completion: id %d, op %d, status: %d\n",
- wr_id, wc->opcode, wc->status);
+ ipoib_dbg_data(priv, "recv completion: id %d, status: %d\n",
+ wr_id, wc->status);
if (unlikely(wr_id >= ipoib_recvq_size)) {
ipoib_warn(priv, "recv completion event with wrid %d (> %d)\n",
@@ -216,7 +216,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
if (wc->slid != priv->local_lid ||
wc->src_qp != priv->qp->qp_num) {
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, IPOIB_ENCAP_LEN);
dev->last_rx = jiffies;
@@ -245,8 +245,8 @@ static void ipoib_ib_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
struct ipoib_tx_buf *tx_req;
unsigned long flags;
- ipoib_dbg_data(priv, "send completion: id %d, op %d, status: %d\n",
- wr_id, wc->opcode, wc->status);
+ ipoib_dbg_data(priv, "send completion: id %d, status: %d\n",
+ wr_id, wc->status);
if (unlikely(wr_id >= ipoib_sendq_size)) {
ipoib_warn(priv, "send completion event with wrid %d (> %d)\n",
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index f2a40ae8e7d0..b4c380c5a3ba 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -395,14 +395,10 @@ static void path_rec_completion(int status,
skb_queue_head_init(&skqueue);
if (!status) {
- struct ib_ah_attr av = {
- .dlid = be16_to_cpu(pathrec->dlid),
- .sl = pathrec->sl,
- .port_num = priv->port,
- .static_rate = pathrec->rate
- };
-
- ah = ipoib_create_ah(dev, priv->pd, &av);
+ struct ib_ah_attr av;
+
+ if (!ib_init_ah_from_path(priv->ca, priv->port, pathrec, &av))
+ ah = ipoib_create_ah(dev, priv->pd, &av);
}
spin_lock_irqsave(&priv->lock, flags);
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index a00fe470a829..bd686a2a517d 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -190,16 +190,14 @@ static void gameport_run_poll_handler(unsigned long d)
* Basic gameport -> driver core mappings
*/
-static void gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
+static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
{
int error;
- down_write(&gameport_bus.subsys.rwsem);
-
gameport->dev.driver = &drv->driver;
if (drv->connect(gameport, drv)) {
gameport->dev.driver = NULL;
- goto out;
+ return -ENODEV;
}
error = device_bind_driver(&gameport->dev);
@@ -211,31 +209,21 @@ static void gameport_bind_driver(struct gameport *gameport, struct gameport_driv
drv->description, error);
drv->disconnect(gameport);
gameport->dev.driver = NULL;
- goto out;
+ return error;
}
- out:
- up_write(&gameport_bus.subsys.rwsem);
-}
-
-static void gameport_release_driver(struct gameport *gameport)
-{
- down_write(&gameport_bus.subsys.rwsem);
- device_release_driver(&gameport->dev);
- up_write(&gameport_bus.subsys.rwsem);
+ return 0;
}
static void gameport_find_driver(struct gameport *gameport)
{
int error;
- down_write(&gameport_bus.subsys.rwsem);
error = device_attach(&gameport->dev);
if (error < 0)
printk(KERN_WARNING
"gameport: device_attach() failed for %s (%s), error: %d\n",
gameport->phys, gameport->name, error);
- up_write(&gameport_bus.subsys.rwsem);
}
@@ -483,13 +471,12 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut
{
struct gameport *gameport = to_gameport_port(dev);
struct device_driver *drv;
- int retval;
+ int error;
- retval = mutex_lock_interruptible(&gameport_mutex);
- if (retval)
- return retval;
+ error = mutex_lock_interruptible(&gameport_mutex);
+ if (error)
+ return error;
- retval = count;
if (!strncmp(buf, "none", count)) {
gameport_disconnect_port(gameport);
} else if (!strncmp(buf, "reconnect", count)) {
@@ -499,15 +486,15 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut
gameport_find_driver(gameport);
} else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
gameport_disconnect_port(gameport);
- gameport_bind_driver(gameport, to_gameport_driver(drv));
+ error = gameport_bind_driver(gameport, to_gameport_driver(drv));
put_driver(drv);
} else {
- retval = -EINVAL;
+ error = -EINVAL;
}
mutex_unlock(&gameport_mutex);
- return retval;
+ return error ? error : count;
}
static struct device_attribute gameport_device_attrs[] = {
@@ -655,7 +642,7 @@ static void gameport_disconnect_port(struct gameport *gameport)
do {
parent = s->parent;
- gameport_release_driver(s);
+ device_release_driver(&s->dev);
gameport_destroy_port(s);
} while ((s = parent) != gameport);
}
@@ -663,7 +650,7 @@ static void gameport_disconnect_port(struct gameport *gameport)
/*
* Ok, no children left, now disconnect this port
*/
- gameport_release_driver(gameport);
+ device_release_driver(&gameport->dev);
}
void gameport_rescan(struct gameport *gameport)
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index a15e531ec755..5895202b972c 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -115,18 +115,18 @@ static int serio_match_port(const struct serio_device_id *ids, struct serio *ser
* Basic serio -> driver core mappings
*/
-static void serio_bind_driver(struct serio *serio, struct serio_driver *drv)
+static int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
{
int error;
- down_write(&serio_bus.subsys.rwsem);
-
if (serio_match_port(drv->id_table, serio)) {
+
serio->dev.driver = &drv->driver;
if (serio_connect_driver(serio, drv)) {
serio->dev.driver = NULL;
- goto out;
+ return -ENODEV;
}
+
error = device_bind_driver(&serio->dev);
if (error) {
printk(KERN_WARNING
@@ -136,31 +136,21 @@ static void serio_bind_driver(struct serio *serio, struct serio_driver *drv)
drv->description, error);
serio_disconnect_driver(serio);
serio->dev.driver = NULL;
- goto out;
+ return error;
}
}
- out:
- up_write(&serio_bus.subsys.rwsem);
-}
-
-static void serio_release_driver(struct serio *serio)
-{
- down_write(&serio_bus.subsys.rwsem);
- device_release_driver(&serio->dev);
- up_write(&serio_bus.subsys.rwsem);
+ return 0;
}
static void serio_find_driver(struct serio *serio)
{
int error;
- down_write(&serio_bus.subsys.rwsem);
error = device_attach(&serio->dev);
if (error < 0)
printk(KERN_WARNING
"serio: device_attach() failed for %s (%s), error: %d\n",
serio->phys, serio->name, error);
- up_write(&serio_bus.subsys.rwsem);
}
@@ -470,13 +460,12 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
{
struct serio *serio = to_serio_port(dev);
struct device_driver *drv;
- int retval;
+ int error;
- retval = mutex_lock_interruptible(&serio_mutex);
- if (retval)
- return retval;
+ error = mutex_lock_interruptible(&serio_mutex);
+ if (error)
+ return error;
- retval = count;
if (!strncmp(buf, "none", count)) {
serio_disconnect_port(serio);
} else if (!strncmp(buf, "reconnect", count)) {
@@ -486,15 +475,15 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
serio_find_driver(serio);
} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
serio_disconnect_port(serio);
- serio_bind_driver(serio, to_serio_driver(drv));
+ error = serio_bind_driver(serio, to_serio_driver(drv));
put_driver(drv);
} else {
- retval = -EINVAL;
+ error = -EINVAL;
}
mutex_unlock(&serio_mutex);
- return retval;
+ return error ? error : count;
}
static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf)
@@ -665,7 +654,7 @@ static void serio_disconnect_port(struct serio *serio)
do {
parent = s->parent;
- serio_release_driver(s);
+ device_release_driver(&s->dev);
serio_destroy_port(s);
} while ((s = parent) != serio);
}
@@ -673,7 +662,7 @@ static void serio_disconnect_port(struct serio *serio)
/*
* Ok, no children left, now disconnect this port
*/
- serio_release_driver(serio);
+ device_release_driver(&serio->dev);
}
void serio_rescan(struct serio *serio)
diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c
index e3e5c1399076..ee2b0b9f8f46 100644
--- a/drivers/isdn/act2000/module.c
+++ b/drivers/isdn/act2000/module.c
@@ -442,7 +442,7 @@ act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb)
return 0;
}
skb_reserve(xmit_skb, 19);
- memcpy(skb_put(xmit_skb, len), skb->data, len);
+ skb_copy_from_linear_data(skb, skb_put(xmit_skb, len), len);
} else {
xmit_skb = skb_clone(skb, GFP_ATOMIC);
if (!xmit_skb) {
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
index 2baef349c12d..c8e1c357cec8 100644
--- a/drivers/isdn/gigaset/usb-gigaset.c
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -652,7 +652,7 @@ static int write_modem(struct cardstate *cs)
* transmit data
*/
count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size);
- memcpy(ucs->bulk_out_buffer, bcs->tx_skb->data, count);
+ skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count);
skb_pull(bcs->tx_skb, count);
atomic_set(&ucs->busy, 1);
gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count);
diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c
index 1e2d38e3d68c..428872b653e9 100644
--- a/drivers/isdn/hardware/avm/b1dma.c
+++ b/drivers/isdn/hardware/avm/b1dma.c
@@ -404,7 +404,8 @@ static void b1dma_dispatch_tx(avmcard *card)
printk(KERN_DEBUG "tx: put 0x%x len=%d\n",
skb->data[2], txlen);
#endif
- memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2);
+ skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
+ skb->len - 2);
}
txlen = (txlen + 3) & ~3;
diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c
index 6f5efa8d78cb..d58f927e766a 100644
--- a/drivers/isdn/hardware/avm/c4.c
+++ b/drivers/isdn/hardware/avm/c4.c
@@ -457,7 +457,8 @@ static void c4_dispatch_tx(avmcard *card)
printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n",
card->name, skb->data[2], txlen);
#endif
- memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2);
+ skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
+ skb->len - 2);
}
txlen = (txlen + 3) & ~3;
diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c
index ae377e812775..1642dca988a1 100644
--- a/drivers/isdn/hisax/elsa_ser.c
+++ b/drivers/isdn/hisax/elsa_ser.c
@@ -254,14 +254,16 @@ write_modem(struct BCState *bcs) {
count = len;
if (count > MAX_MODEM_BUF - fp) {
count = MAX_MODEM_BUF - fp;
- memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count);
+ skb_copy_from_linear_data(bcs->tx_skb,
+ cs->hw.elsa.transbuf + fp, count);
skb_pull(bcs->tx_skb, count);
cs->hw.elsa.transcnt += count;
ret = count;
count = len - count;
fp = 0;
}
- memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count);
+ skb_copy_from_linear_data(bcs->tx_skb,
+ cs->hw.elsa.transbuf + fp, count);
skb_pull(bcs->tx_skb, count);
cs->hw.elsa.transcnt += count;
ret += count;
diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c
index cd3b5ad53491..3446f249d675 100644
--- a/drivers/isdn/hisax/isdnl2.c
+++ b/drivers/isdn/hisax/isdnl2.c
@@ -1293,7 +1293,8 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
oskb = skb;
skb = alloc_skb(oskb->len + i, GFP_ATOMIC);
memcpy(skb_put(skb, i), header, i);
- memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
+ skb_copy_from_linear_data(oskb,
+ skb_put(skb, oskb->len), oskb->len);
dev_kfree_skb(oskb);
}
st->l2.l2l1(st, PH_PULL | INDICATION, skb);
diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c
index b2ae4ec1e49e..f85450146bdc 100644
--- a/drivers/isdn/hysdn/hycapi.c
+++ b/drivers/isdn/hysdn/hycapi.c
@@ -398,8 +398,9 @@ static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
_len = CAPIMSG_LEN(skb->data);
if (_len > 22) {
_len2 = _len - 22;
- memcpy(msghead, skb->data, 22);
- memcpy(skb->data + _len2, msghead, 22);
+ skb_copy_from_linear_data(skb, msghead, 22);
+ skb_copy_to_linear_data_offset(skb, _len2,
+ msghead, 22);
skb_pull(skb, _len2);
CAPIMSG_SETLEN(skb->data, 22);
retval = capilib_data_b3_req(&cinfo->ncci_head,
diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c
index 557d96c78a62..cfa8fa5e44ab 100644
--- a/drivers/isdn/hysdn/hysdn_net.c
+++ b/drivers/isdn/hysdn/hysdn_net.c
@@ -214,8 +214,6 @@ hysdn_rx_netpkt(hysdn_card * card, unsigned char *buf, unsigned short len)
lp->stats.rx_dropped++;
return;
}
- skb->dev = &lp->netdev;
-
/* copy the data */
memcpy(skb_put(skb, len), buf, len);
diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c
index b7b5aa4748a0..81db4a190d41 100644
--- a/drivers/isdn/hysdn/hysdn_sched.c
+++ b/drivers/isdn/hysdn/hysdn_sched.c
@@ -113,7 +113,8 @@ hysdn_sched_tx(hysdn_card *card, unsigned char *buf,
(skb = hysdn_tx_netget(card)) != NULL)
{
if (skb->len <= maxlen) {
- memcpy(buf, skb->data, skb->len); /* copy the packet to the buffer */
+ /* copy the packet to the buffer */
+ skb_copy_from_linear_data(skb, buf, skb->len);
*len = skb->len;
*chan = CHAN_NDIS_DATA;
card->net_tx_busy = 1; /* we are busy sending network data */
@@ -126,7 +127,7 @@ hysdn_sched_tx(hysdn_card *card, unsigned char *buf,
((skb = hycapi_tx_capiget(card)) != NULL) )
{
if (skb->len <= maxlen) {
- memcpy(buf, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, buf, skb->len);
*len = skb->len;
*chan = CHAN_CAPI;
hycapi_tx_capiack(card);
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
index 9c926e41b114..c97330b19877 100644
--- a/drivers/isdn/i4l/isdn_common.c
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -829,7 +829,7 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_que
dflag = 0;
}
count_put = count_pull;
- memcpy(cp, skb->data, count_put);
+ skb_copy_from_linear_data(skb, cp, count_put);
cp += count_put;
len -= count_put;
#ifdef CONFIG_ISDN_AUDIO
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 838b3734e2b6..aa83277aba74 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -872,7 +872,8 @@ typedef struct {
static void
isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
{
- u_char *p = skb->nh.raw; /* hopefully, this was set correctly */
+ /* hopefully, this was set correctly */
+ const u_char *p = skb_network_header(skb);
unsigned short proto = ntohs(skb->protocol);
int data_ofs;
ip_ports *ipp;
@@ -880,7 +881,7 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
addinfo[0] = '\0';
/* This check stolen from 2.1.72 dev_queue_xmit_nit() */
- if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
+ if (p < skb->data || skb->network_header >= skb->tail) {
/* fall back to old isdn_net_log_packet method() */
char * buf = skb->data;
@@ -1121,7 +1122,7 @@ isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
if (!skb)
return;
if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
- int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
+ const int pullsize = skb_network_offset(skb) - ETH_HLEN;
if (pullsize > 0) {
printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
skb_pull(skb, pullsize);
@@ -1366,7 +1367,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev)
struct ethhdr *eth;
unsigned char *rawp;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN);
eth = eth_hdr(skb);
@@ -1786,7 +1787,7 @@ isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
}
skb->dev = ndev;
skb->pkt_type = PACKET_HOST;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
#ifdef ISDN_DEBUG_NET_DUMP
isdn_dumppkt("R:", skb->data, skb->len, 40);
#endif
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 1b2df80c3bce..387392cb3d68 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -1100,7 +1100,8 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
goto drop_packet;
}
skb_put(skb, skb_old->len + 128);
- memcpy(skb->data, skb_old->data, skb_old->len);
+ skb_copy_from_linear_data(skb_old, skb->data,
+ skb_old->len);
if (net_dev->local->ppp_slot < 0) {
printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n",
__FUNCTION__, net_dev->local->ppp_slot);
@@ -1167,7 +1168,7 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
mlp->huptimer = 0;
#endif /* CONFIG_IPPP_FILTER */
skb->dev = dev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
netif_rx(skb);
/* net_dev->local->stats.rx_packets++; done in isdn_net.c */
return;
@@ -1902,7 +1903,9 @@ void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
while( from != to ) {
unsigned int len = from->len - MP_HEADER_LEN;
- memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
+ skb_copy_from_linear_data_offset(from, MP_HEADER_LEN,
+ skb_put(skb,len),
+ len);
frag = from->next;
isdn_ppp_mp_free_skb(mp, from);
from = frag;
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
index e3add27dd0e1..e93ad59f60bf 100644
--- a/drivers/isdn/isdnloop/isdnloop.c
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -415,7 +415,8 @@ isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card)
spin_lock_irqsave(&card->isdnloop_lock, flags);
nskb = dev_alloc_skb(skb->len);
if (nskb) {
- memcpy(skb_put(nskb, len), skb->data, len);
+ skb_copy_from_linear_data(skb,
+ skb_put(nskb, len), len);
skb_queue_tail(&card->bqueue[channel], nskb);
dev_kfree_skb(skb);
} else
diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c
index 47c59e95898d..7b55e151f1b0 100644
--- a/drivers/isdn/pcbit/capi.c
+++ b/drivers/isdn/pcbit/capi.c
@@ -429,8 +429,9 @@ int capi_decode_conn_ind(struct pcbit_chan * chan,
if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC)))
return -1;
- memcpy(info->data.setup.CallingPN, skb->data + count + 1,
- len - count);
+ skb_copy_from_linear_data_offset(skb, count + 1,
+ info->data.setup.CallingPN,
+ len - count);
info->data.setup.CallingPN[len - count] = 0;
}
@@ -457,8 +458,9 @@ int capi_decode_conn_ind(struct pcbit_chan * chan,
if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC)))
return -1;
- memcpy(info->data.setup.CalledPN, skb->data + count + 1,
- len - count);
+ skb_copy_from_linear_data_offset(skb, count + 1,
+ info->data.setup.CalledPN,
+ len - count);
info->data.setup.CalledPN[len - count] = 0;
}
@@ -539,7 +541,7 @@ int capi_decode_conn_actv_ind(struct pcbit_chan * chan, struct sk_buff *skb)
#ifdef DEBUG
if (len > 1 && len < 31) {
- memcpy(str, skb->data + 2, len - 1);
+ skb_copy_from_linear_data_offset(skb, 2, str, len - 1);
str[len] = 0;
printk(KERN_DEBUG "Connected Party Number: %s\n", str);
}
diff --git a/drivers/kvm/mmu.c b/drivers/kvm/mmu.c
index e85b4c7c36f7..cab26f301eab 100644
--- a/drivers/kvm/mmu.c
+++ b/drivers/kvm/mmu.c
@@ -1171,6 +1171,7 @@ void kvm_mmu_pre_write(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes)
* and zap two pdes instead of one.
*/
if (level == PT32_ROOT_LEVEL) {
+ page_offset &= ~7; /* kill rounding error */
page_offset <<= 1;
npte = 2;
}
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 135f22eb1ad4..a98a328b1cfc 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -1259,9 +1259,9 @@ static int smu_release(struct inode *inode, struct file *file)
set_current_state(TASK_UNINTERRUPTIBLE);
if (pp->cmd.status != 1)
break;
- spin_lock_irqsave(&pp->lock, flags);
- schedule();
spin_unlock_irqrestore(&pp->lock, flags);
+ schedule();
+ spin_lock_irqsave(&pp->lock, flags);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&pp->wait, &wait);
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 5554adaa58f9..e61e0efe9ec7 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -863,9 +863,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
/* We need 4 bits per page, rounded up to a multiple of sizeof(unsigned long) */
bitmap->filemap_attr = kzalloc(
- (((num_pages*4/8)+sizeof(unsigned long)-1)
- /sizeof(unsigned long))
- *sizeof(unsigned long),
+ roundup( DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)),
GFP_KERNEL);
if (!bitmap->filemap_attr)
goto out;
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index 03b47a262f27..cbd1184b5219 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -667,7 +667,7 @@ IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR_KEYTAB_SIZE] = {
[ 0x1f ] = KEY_L,
[ 0x2b ] = KEY_I,
- [ 0x2d ] = KEY_ZOOM,
+ [ 0x2d ] = KEY_SCREEN,
[ 0x1e ] = KEY_ZOOM,
[ 0x1b ] = KEY_VOLUMEUP,
[ 0x0f ] = KEY_VOLUMEDOWN,
@@ -682,12 +682,12 @@ IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR_KEYTAB_SIZE] = {
[ 0x3f ] = KEY_UP,
[ 0x3e ] = KEY_DOWN,
- [ 0x1a ] = KEY_PAUSE,
+ [ 0x1a ] = KEY_ENTER,
[ 0x1d ] = KEY_MENU,
- [ 0x19 ] = KEY_PLAY,
- [ 0x16 ] = KEY_REWIND,
- [ 0x13 ] = KEY_FORWARD,
+ [ 0x19 ] = KEY_AGAIN,
+ [ 0x16 ] = KEY_PREVIOUSSONG,
+ [ 0x13 ] = KEY_NEXTSONG,
[ 0x15 ] = KEY_PAUSE,
[ 0x0e ] = KEY_REWIND,
[ 0x0d ] = KEY_PLAY,
@@ -1739,7 +1739,7 @@ IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE] = {
EXPORT_SYMBOL_GPL(ir_codes_encore_enltv);
-/* for the Technotrend 1500 bundled remote: */
+/* for the Technotrend 1500 bundled remotes (grey and black): */
IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = {
[ 0x01 ] = KEY_POWER,
[ 0x02 ] = KEY_SHUFFLE, /* ? double-arrow key */
@@ -1774,6 +1774,12 @@ IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = {
[ 0x25 ] = KEY_VOLUMEUP,
[ 0x26 ] = KEY_VOLUMEDOWN,
[ 0x27 ] = KEY_SETUP,
+ [ 0x3a ] = KEY_RECORD, /* these keys are only in the black remote */
+ [ 0x3b ] = KEY_PLAY,
+ [ 0x3c ] = KEY_STOP,
+ [ 0x3d ] = KEY_REWIND,
+ [ 0x3e ] = KEY_PAUSE,
+ [ 0x3f ] = KEY_FORWARD,
};
EXPORT_SYMBOL_GPL(ir_codes_tt_1500);
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index 7e0cedc557df..e3d04a4cef4d 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -1428,6 +1428,7 @@ static void video_close(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
struct saa7146_vv *vv = dev->vv_data;
+ struct videobuf_queue *q = &fh->video_q;
int err;
if (IS_CAPTURE_ACTIVE(fh) != 0) {
@@ -1436,6 +1437,11 @@ static void video_close(struct saa7146_dev *dev, struct file *file)
err = saa7146_stop_preview(fh);
}
+ // release all capture buffers
+ mutex_lock(&q->lock);
+ videobuf_read_stop(q);
+ mutex_unlock(&q->lock);
+
/* hmm, why is this function declared void? */
/* return err */
}
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
index 79875958930e..a0dcd59da76e 100644
--- a/drivers/media/dvb/b2c2/Kconfig
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -9,7 +9,6 @@ config DVB_B2C2_FLEXCOP
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_BCM3510 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_TUNER_LGH06XF 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 752cf79c532f..b02c2fd65baa 100644
--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
@@ -14,7 +14,6 @@
#include "stv0297.h"
#include "mt312.h"
#include "lgdt330x.h"
-#include "lgh06xf.h"
#include "dvb-pll.h"
/* lnb control */
@@ -507,7 +506,7 @@ int flexcop_frontend_init(struct flexcop_device *fc)
/* try the air atsc 3nd generation (lgdt3303) */
if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC3;
- dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap);
+ dvb_attach(dvb_pll_attach, fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_lg_tdvs_h06xf);
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 */
diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c
index 6e166801505d..01af4d237eb1 100644
--- a/drivers/media/dvb/b2c2/flexcop-pci.c
+++ b/drivers/media/dvb/b2c2/flexcop-pci.c
@@ -127,10 +127,11 @@ static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)
{
struct flexcop_pci *fc_pci = dev_id;
struct flexcop_device *fc = fc_pci->fc_dev;
+ unsigned long flags;
flexcop_ibi_value v;
irqreturn_t ret = IRQ_HANDLED;
- spin_lock_irq(&fc_pci->irq_lock);
+ spin_lock_irqsave(&fc_pci->irq_lock,flags);
v = fc->read_ibi_reg(fc,irq_20c);
@@ -194,7 +195,7 @@ static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)
ret = IRQ_NONE;
}
- spin_unlock_irq(&fc_pci->irq_lock);
+ spin_unlock_irqrestore(&fc_pci->irq_lock,flags);
return ret;
}
@@ -293,12 +294,12 @@ static int flexcop_pci_init(struct flexcop_pci *fc_pci)
}
pci_set_drvdata(fc_pci->pdev, fc_pci);
-
+ spin_lock_init(&fc_pci->irq_lock);
if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr,
IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0)
goto err_pci_iounmap;
- spin_lock_init(&fc_pci->irq_lock);
+
fc_pci->init_state |= FC_PCI_INIT;
return ret;
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
index dd66b60fbc98..cfd6fb729a61 100644
--- a/drivers/media/dvb/bt8xx/Kconfig
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -7,7 +7,7 @@ config DVB_BT8XX
select DVB_CX24110 if !DVB_FE_CUSTOMISE
select DVB_OR51211 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
+ select DVB_PLL
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select FW_LOADER
help
diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c
index 83b090ef2445..df72b4b8ee10 100644
--- a/drivers/media/dvb/bt8xx/bt878.c
+++ b/drivers/media/dvb/bt8xx/bt878.c
@@ -393,9 +393,7 @@ static struct cards card_list[] __devinitdata = {
{ 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" },
{ 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" },
{ 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV" },
- { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" },
-
- { 0, -1, NULL }
+ { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" }
};
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index 58f69f6ae391..4f1c09bee538 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -610,7 +610,8 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
lgdt330x_reset(card);
card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
if (card->fe != NULL) {
- dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter);
+ dvb_attach(dvb_pll_attach, card->fe, 0x61,
+ card->i2c_adapter, &dvb_pll_lg_tdvs_h06xf);
dprintk ("dvb_bt8xx: lgdt330x detected\n");
}
break;
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
index e75f4173c059..436880e68672 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
@@ -37,8 +37,8 @@
#include "cx24110.h"
#include "or51211.h"
#include "lgdt330x.h"
-#include "lgh06xf.h"
#include "zl10353.h"
+#include "dvb-pll.h"
struct dvb_bt8xx_card {
struct mutex lock;
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index a5c0e1a3e6d1..275df65fde99 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -132,6 +132,11 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
if ((file->f_flags & O_ACCMODE) == O_RDWR) {
if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
mutex_unlock(&dmxdev->mutex);
@@ -171,6 +176,7 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
dmxdev->demux->disconnect_frontend(dmxdev->demux);
dmxdev->demux->connect_frontend(dmxdev->demux, front);
}
+ dvbdev->users++;
mutex_unlock(&dmxdev->mutex);
return 0;
}
@@ -198,7 +204,16 @@ static int dvb_dvr_release(struct inode *inode, struct file *file)
vfree(mem);
}
}
- mutex_unlock(&dmxdev->mutex);
+ /* TODO */
+ dvbdev->users--;
+ if(dvbdev->users==-1 && dmxdev->exit==1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ wake_up(&dvbdev->wait_queue);
+ } else
+ mutex_unlock(&dmxdev->mutex);
+
return 0;
}
@@ -215,6 +230,11 @@ static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
return -EINVAL;
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
+
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
ret = dmxdev->demux->write(dmxdev->demux, buf, count);
mutex_unlock(&dmxdev->mutex);
return ret;
@@ -227,6 +247,11 @@ static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
struct dmxdev *dmxdev = dvbdev->priv;
int ret;
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
//mutex_lock(&dmxdev->mutex);
ret = dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
file->f_flags & O_NONBLOCK,
@@ -665,6 +690,8 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
dmxdevfilter->feed.ts = NULL;
init_timer(&dmxdevfilter->timer);
+ dvbdev->users++;
+
mutex_unlock(&dmxdev->mutex);
return 0;
}
@@ -943,7 +970,21 @@ static int dvb_demux_release(struct inode *inode, struct file *file)
struct dmxdev_filter *dmxdevfilter = file->private_data;
struct dmxdev *dmxdev = dmxdevfilter->dev;
- return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+ int ret;
+
+ ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+
+ mutex_lock(&dmxdev->mutex);
+ dmxdev->dvbdev->users--;
+ if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ wake_up(&dmxdev->dvbdev->wait_queue);
+ } else
+ mutex_unlock(&dmxdev->mutex);
+
+ return ret;
}
static struct file_operations dvb_demux_fops = {
@@ -1027,6 +1068,7 @@ static struct file_operations dvb_dvr_fops = {
static struct dvb_device dvbdev_dvr = {
.priv = NULL,
.readers = 1,
+ .users = 1,
.fops = &dvb_dvr_fops
};
@@ -1064,6 +1106,16 @@ EXPORT_SYMBOL(dvb_dmxdev_init);
void dvb_dmxdev_release(struct dmxdev *dmxdev)
{
+ dmxdev->exit=1;
+ if (dmxdev->dvbdev->users > 1) {
+ wait_event(dmxdev->dvbdev->wait_queue,
+ dmxdev->dvbdev->users==1);
+ }
+ if (dmxdev->dvr_dvbdev->users > 1) {
+ wait_event(dmxdev->dvr_dvbdev->wait_queue,
+ dmxdev->dvr_dvbdev->users==1);
+ }
+
dvb_unregister_device(dmxdev->dvbdev);
dvb_unregister_device(dmxdev->dvr_dvbdev);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index d2bee9ffe43c..29746e70d325 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -91,6 +91,8 @@ struct dmxdev {
int filternum;
int capabilities;
+
+ unsigned int exit:1;
#define DMXDEV_CAP_DUPLEX 1
struct dmx_frontend *dvr_orig_fe;
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index a21a894d3f98..f4e4ca2dcade 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -606,6 +606,7 @@ static void dvb_frontend_stop(struct dvb_frontend *fe)
return;
kthread_stop(fepriv->thread);
+
init_MUTEX (&fepriv->sem);
fepriv->state = FESTATE_IDLE;
@@ -1023,6 +1024,7 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
struct dvb_device *dvbdev = file->private_data;
struct dvb_frontend *fe = dvbdev->priv;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int ret;
dprintk ("%s\n", __FUNCTION__);
@@ -1032,7 +1034,14 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
if (fe->ops.ts_bus_ctrl)
fe->ops.ts_bus_ctrl (fe, 0);
- return dvb_generic_release (inode, file);
+ ret = dvb_generic_release (inode, file);
+
+ if (dvbdev->users==-1 && fepriv->exit==1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ wake_up(&dvbdev->wait_queue);
+ }
+ return ret;
}
static struct file_operations dvb_frontend_fops = {
@@ -1092,8 +1101,15 @@ int dvb_unregister_frontend(struct dvb_frontend* fe)
dprintk ("%s\n", __FUNCTION__);
mutex_lock(&frontend_mutex);
- dvb_unregister_device (fepriv->dvbdev);
dvb_frontend_stop (fe);
+ mutex_unlock(&frontend_mutex);
+
+ if (fepriv->dvbdev->users < -1)
+ wait_event(fepriv->dvbdev->wait_queue,
+ fepriv->dvbdev->users==-1);
+
+ mutex_lock(&frontend_mutex);
+ dvb_unregister_device (fepriv->dvbdev);
/* fe is invalid now */
kfree(fepriv);
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
index 76e9c36597eb..4ebf33a5ffa2 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -174,7 +174,7 @@ static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb,
struct ethhdr *eth;
unsigned char *rawp;
- skb->mac.raw=skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb,dev->hard_header_len);
eth = eth_hdr(skb);
@@ -600,6 +600,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
/* Check CRC32, we've got it in our skb already. */
unsigned short ulen = htons(priv->ule_sndu_len);
unsigned short utype = htons(priv->ule_sndu_type);
+ const u8 *tail;
struct kvec iov[3] = {
{ &ulen, sizeof ulen },
{ &utype, sizeof utype },
@@ -613,10 +614,11 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
}
ule_crc = iov_crc32(ule_crc, iov, 3);
- expected_crc = *((u8 *)priv->ule_skb->tail - 4) << 24 |
- *((u8 *)priv->ule_skb->tail - 3) << 16 |
- *((u8 *)priv->ule_skb->tail - 2) << 8 |
- *((u8 *)priv->ule_skb->tail - 1);
+ tail = skb_tail_pointer(priv->ule_skb);
+ expected_crc = *(tail - 4) << 24 |
+ *(tail - 3) << 16 |
+ *(tail - 2) << 8 |
+ *(tail - 1);
if (ule_crc != expected_crc) {
printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n",
priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0);
@@ -695,7 +697,9 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
}
else
{
- memcpy(dest_addr, priv->ule_skb->data, ETH_ALEN);
+ skb_copy_from_linear_data(priv->ule_skb,
+ dest_addr,
+ ETH_ALEN);
skb_pull(priv->ule_skb, ETH_ALEN);
}
}
@@ -1435,11 +1439,36 @@ static int dvb_net_ioctl(struct inode *inode, struct file *file,
return dvb_usercopy(inode, file, cmd, arg, dvb_net_do_ioctl);
}
+static int dvb_net_close(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_net *dvbnet = dvbdev->priv;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ dvbdev->readers++;
+ } else {
+ dvbdev->writers++;
+ }
+
+ dvbdev->users++;
+
+ if(dvbdev->users == 1 && dvbnet->exit==1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ wake_up(&dvbdev->wait_queue);
+ }
+ return 0;
+}
+
+
static struct file_operations dvb_net_fops = {
.owner = THIS_MODULE,
.ioctl = dvb_net_ioctl,
.open = dvb_generic_open,
- .release = dvb_generic_release,
+ .release = dvb_net_close,
};
static struct dvb_device dvbdev_net = {
@@ -1454,6 +1483,11 @@ void dvb_net_release (struct dvb_net *dvbnet)
{
int i;
+ dvbnet->exit = 1;
+ if (dvbnet->dvbdev->users < 1)
+ wait_event(dvbnet->dvbdev->wait_queue,
+ dvbnet->dvbdev->users==1);
+
dvb_unregister_device(dvbnet->dvbdev);
for (i=0; i<DVB_NET_DEVICES_MAX; i++) {
diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb/dvb-core/dvb_net.h
index f14e4ca38570..3a3126cae03b 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.h
+++ b/drivers/media/dvb/dvb-core/dvb_net.h
@@ -36,6 +36,7 @@ struct dvb_net {
struct dvb_device *dvbdev;
struct net_device *device[DVB_NET_DEVICES_MAX];
int state[DVB_NET_DEVICES_MAX];
+ unsigned int exit:1;
struct dmx_demux *demux;
};
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
index 14a372a0fe8b..e23d8a0ea1d3 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.c
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -233,6 +233,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
dvbdev->adapter = adap;
dvbdev->priv = priv;
dvbdev->fops = dvbdevfops;
+ init_waitqueue_head (&dvbdev->wait_queue);
memcpy(dvbdev->fops, template->fops, sizeof(struct file_operations));
dvbdev->fops->owner = adap->module;
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
index 620e7887b3d3..6dff10ebf470 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -69,6 +69,7 @@ struct dvb_device {
int writers;
int users;
+ wait_queue_head_t wait_queue;
/* don't really need those !? -- FIXME: use video_usercopy */
int (*kernel_ioctl)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg);
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 80f67a51b908..54488737a08f 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -33,6 +33,7 @@ config DVB_USB_A800
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_PLL
select DVB_DIB3000MB
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
@@ -88,6 +89,7 @@ config DVB_USB_DIB0700
config DVB_USB_UMT_010
tristate "HanfTek UMT-010 DVB-T USB2.0 support"
depends on DVB_USB
+ select DVB_PLL
select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
@@ -96,9 +98,9 @@ config DVB_USB_UMT_010
config DVB_USB_CXUSB
tristate "Conexant USB2.0 hybrid reference design support"
depends on DVB_USB
+ select DVB_PLL
select DVB_CX22702 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
help
@@ -140,6 +142,7 @@ config DVB_USB_AU6610
config DVB_USB_DIGITV
tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support"
depends on DVB_USB
+ select DVB_PLL
select DVB_NXT6000 if !DVB_FE_CUSTOMISE
select DVB_MT352 if !DVB_FE_CUSTOMISE
help
@@ -208,3 +211,10 @@ config DVB_USB_DTT200U
The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan).
The WT-220U and its clones are pen-sized.
+
+config DVB_USB_OPERA1
+ tristate "Opera1 DVB-S USB2.0 receiver"
+ depends on DVB_USB
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the Opera DVB-S USB2.0 receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 40f28f559b54..976f840cc904 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -51,4 +51,8 @@ obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o
dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o
obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
+dvb-usb-opera-objs = opera1.o
+obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o
+
+
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c
index 0dc66a8d2baf..18e0b16fb2a9 100644
--- a/drivers/media/dvb/dvb-usb/au6610.c
+++ b/drivers/media/dvb/dvb-usb/au6610.c
@@ -40,7 +40,7 @@ static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
}
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
- USB_TYPE_VENDOR|USB_DIR_IN, addr, index, usb_buf,
+ USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf,
sizeof(usb_buf), AU6610_USB_TIMEOUT);
if (ret < 0)
@@ -124,7 +124,7 @@ static int au6610_identify_state(struct usb_device *udev,
}
static struct zl10353_config au6610_zl10353_config = {
- .demod_address = 0x1e,
+ .demod_address = 0x0f,
.no_tuner = 1,
.parallel_ts = 1,
};
@@ -140,7 +140,7 @@ static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
}
static struct qt1010_config au6610_qt1010_config = {
- .i2c_address = 0xc4
+ .i2c_address = 0x62
};
static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 127a94b9a1b5..bac2ae3b4a1f 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -27,7 +27,6 @@
#include "cx22702.h"
#include "lgdt330x.h"
-#include "lgh06xf.h"
#include "mt352.h"
#include "mt352_priv.h"
#include "zl10353.h"
@@ -388,7 +387,8 @@ static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap)
static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
{
- dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap);
+ dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap,
+ &dvb_pll_lg_tdvs_h06xf);
return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 6a4d150784a6..dddf164f269a 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -56,10 +56,6 @@ static int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u
if (txlen > 3)
index |= tx[3];
- /* think about swapping here */
- value = le16_to_cpu(value);
- index = le16_to_cpu(index);
-
status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0],
USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen,
USB_CTRL_GET_TIMEOUT);
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 148386aba275..97715f7514d6 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -13,6 +13,7 @@
#define USB_VID_ADSTECH 0x06e1
#define USB_VID_ALCOR_MICRO 0x058f
#define USB_VID_ANCHOR 0x0547
+#define USB_VID_ANUBIS_ELECTRONIC 0x10fd
#define USB_VID_AVERMEDIA 0x07ca
#define USB_VID_COMPRO 0x185b
#define USB_VID_COMPRO_UNK 0x145f
@@ -31,6 +32,7 @@
#define USB_VID_LITEON 0x04ca
#define USB_VID_MEDION 0x1660
#define USB_VID_MSI 0x0db0
+#define USB_VID_OPERA1 0x695c
#define USB_VID_PINNACLE 0x2304
#define USB_VID_VISIONPLUS 0x13d3
#define USB_VID_TWINHAN 0x1822
@@ -127,6 +129,7 @@
#define USB_PID_KYE_DVB_T_WARM 0x701f
#define USB_PID_PCTV_200E 0x020e
#define USB_PID_PCTV_400E 0x020f
+#define USB_PID_PCTV_450E 0x0222
#define USB_PID_LITEON_DVB_T_COLD 0xf000
#define USB_PID_LITEON_DVB_T_WARM 0xf001
#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360
@@ -139,6 +142,9 @@
#define USB_PID_GENPIX_8PSK_COLD 0x0200
#define USB_PID_GENPIX_8PSK_WARM 0x0201
#define USB_PID_SIGMATEK_DVB_110 0x6610
+#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513
+#define USB_PID_OPERA1_COLD 0x2830
+#define USB_PID_OPERA1_WARM 0x3829
#endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
index 9511a31c8f50..68ed3a788083 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
@@ -107,8 +107,6 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
return -ENOMEM;
input_dev->evbit[0] = BIT(EV_KEY);
- input_dev->keycodesize = sizeof(unsigned char);
- input_dev->keycodemax = KEY_MAX;
input_dev->name = "IR-receiver inside an USB DVB receiver";
input_dev->phys = d->rc_phys;
usb_to_input_id(d->udev, &input_dev->id);
diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c
index c9f38a5e70d3..e0587e663591 100644
--- a/drivers/media/dvb/dvb-usb/gl861.c
+++ b/drivers/media/dvb/dvb-usb/gl861.c
@@ -12,7 +12,7 @@
#include "qt1010.h"
/* debug */
-int dvb_usb_gl861_debug;
+static int dvb_usb_gl861_debug;
module_param_named(debug,dvb_usb_gl861_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
@@ -20,7 +20,7 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u16 index;
- u16 value = addr << 8;
+ u16 value = addr << (8 + 1);
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 req, type;
@@ -101,7 +101,7 @@ static int gl861_identify_state(struct usb_device *udev,
}
static struct zl10353_config gl861_zl10353_config = {
- .demod_address = 0x1e,
+ .demod_address = 0x0f,
.no_tuner = 1,
.parallel_ts = 1,
};
@@ -117,7 +117,7 @@ static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
}
static struct qt1010_config gl861_qt1010_config = {
- .i2c_address = 0xc4
+ .i2c_address = 0x62
};
static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c
index d48b24d9abf4..45d7bc214c18 100644
--- a/drivers/media/dvb/dvb-usb/m920x.c
+++ b/drivers/media/dvb/dvb-usb/m920x.c
@@ -14,6 +14,8 @@
#include "mt352.h"
#include "mt352_priv.h"
#include "qt1010.h"
+#include "tda1004x.h"
+#include "tda827x.h"
/* debug */
static int dvb_usb_m920x_debug;
@@ -47,11 +49,15 @@ static inline int m9206_read(struct usb_device *udev, u8 request, u16 value,\
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
request, USB_TYPE_VENDOR | USB_DIR_IN,
value, index, data, size, 2000);
- if (ret < 0)
+ if (ret < 0) {
+ printk(KERN_INFO "m920x_read = error: %d\n", ret);
return ret;
+ }
- if (ret != size)
+ if (ret != size) {
+ deb_rc("m920x_read = no data\n");
return -EIO;
+ }
return 0;
}
@@ -64,19 +70,22 @@ static inline int m9206_write(struct usb_device *udev, u8 request,
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
request, USB_TYPE_VENDOR | USB_DIR_OUT,
value, index, NULL, 0, 2000);
+
return ret;
}
-static int m9206_rc_init(struct usb_device *udev)
+static int m9206_init(struct dvb_usb_device *d)
{
int ret = 0;
/* Remote controller init. */
- if ((ret = m9206_write(udev, M9206_CORE, 0xa8, M9206_RC_INIT2)) != 0)
- return ret;
+ if (d->props.rc_query) {
+ if ((ret = m9206_write(d->udev, M9206_CORE, 0xa8, M9206_RC_INIT2)) != 0)
+ return ret;
- if ((ret = m9206_write(udev, M9206_CORE, 0x51, M9206_RC_INIT1)) != 0)
- return ret;
+ if ((ret = m9206_write(d->udev, M9206_CORE, 0x51, M9206_RC_INIT1)) != 0)
+ return ret;
+ }
return ret;
}
@@ -87,16 +96,15 @@ static int m9206_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
int i, ret = 0;
u8 rc_state[2];
-
if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0)
goto unlock;
if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0)
goto unlock;
- for (i = 0; i < ARRAY_SIZE(megasky_rc_keys); i++)
- if (megasky_rc_keys[i].data == rc_state[1]) {
- *event = megasky_rc_keys[i].event;
+ for (i = 0; i < d->props.rc_key_map_size; i++)
+ if (d->props.rc_key_map[i].data == rc_state[1]) {
+ *event = d->props.rc_key_map[i].event;
switch(rc_state[0]) {
case 0x80:
@@ -137,53 +145,51 @@ static int m9206_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct m9206_state *m = d->priv;
- int i;
+ int i, j;
int ret = 0;
+ if (!num)
+ return -EINVAL;
+
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
- if (num > 2)
- return -EINVAL;
-
for (i = 0; i < num; i++) {
- if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].addr, 0x80)) != 0)
- goto unlock;
-
- if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[0], 0x0)) != 0)
+ if (msg[i].flags & (I2C_M_NO_RD_ACK|I2C_M_IGNORE_NAK|I2C_M_TEN) ||
+ msg[i].len == 0) {
+ /* For a 0 byte message, I think sending the address to index 0x80|0x40
+ * would be the correct thing to do. However, zero byte messages are
+ * only used for probing, and since we don't know how to get the slave's
+ * ack, we can't probe. */
+ ret = -ENOTSUPP;
goto unlock;
-
- if (i + 1 < num && msg[i + 1].flags & I2C_M_RD) {
- int i2c_i;
-
- for (i2c_i = 0; i2c_i < M9206_I2C_MAX; i2c_i++)
- if (msg[i].addr == m->i2c_r[i2c_i].addr)
- break;
-
- if (i2c_i >= M9206_I2C_MAX) {
- deb_rc("No magic for i2c addr!\n");
- ret = -EINVAL;
+ }
+ /* Send START & address/RW bit */
+ if (!(msg[i].flags & I2C_M_NOSTART)) {
+ if ((ret = m9206_write(d->udev, M9206_I2C, (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), 0x80)) != 0)
goto unlock;
+ /* Should check for ack here, if we knew how. */
+ }
+ if (msg[i].flags & I2C_M_RD) {
+ for (j = 0; j < msg[i].len; j++) {
+ /* Last byte of transaction? Send STOP, otherwise send ACK. */
+ int stop = (i+1 == num && j+1 == msg[i].len)?0x40:0x01;
+ if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x20|stop, &msg[i].buf[j], 1)) != 0)
+ goto unlock;
}
-
- if ((ret = m9206_write(d->udev, M9206_I2C, m->i2c_r[i2c_i].magic, 0x80)) != 0)
- goto unlock;
-
- if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x60, msg[i + 1].buf, msg[i + 1].len)) != 0)
- goto unlock;
-
- i++;
} else {
- if (msg[i].len != 2)
- return -EINVAL;
-
- if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[1], 0x40)) != 0)
- goto unlock;
+ for (j = 0; j < msg[i].len; j++) {
+ /* Last byte of transaction? Then send STOP. */
+ int stop = (i+1 == num && j+1 == msg[i].len)?0x40:0x00;
+ if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[j], stop)) != 0)
+ goto unlock;
+ /* Should check for ack here too. */
+ }
}
}
- ret = i;
- unlock:
+ ret = num;
+
+unlock:
mutex_unlock(&d->i2c_mutex);
return ret;
@@ -324,6 +330,7 @@ static int m9206_firmware_download(struct usb_device *udev,
i += size;
}
if (i != fw->size) {
+ deb_rc("bad firmware file!\n");
ret = -EINVAL;
goto done;
}
@@ -342,10 +349,10 @@ static int m9206_firmware_download(struct usb_device *udev,
}
/* Callbacks for DVB USB */
-static int megasky_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
+static int m920x_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
{
struct usb_host_interface *alt;
@@ -381,20 +388,15 @@ static int megasky_mt352_demod_init(struct dvb_frontend *fe)
}
static struct mt352_config megasky_mt352_config = {
- .demod_address = 0x1e,
+ .demod_address = 0x0f,
.no_tuner = 1,
.demod_init = megasky_mt352_demod_init,
};
static int megasky_mt352_frontend_attach(struct dvb_usb_adapter *adap)
{
- struct m9206_state *m = adap->dev->priv;
-
deb_rc("megasky_frontend_attach!\n");
- m->i2c_r[M9206_I2C_DEMOD].addr = megasky_mt352_config.demod_address;
- m->i2c_r[M9206_I2C_DEMOD].magic = 0x1f;
-
if ((adap->fe = dvb_attach(mt352_attach, &megasky_mt352_config, &adap->dev->i2c_adap)) == NULL)
return -EIO;
@@ -402,16 +404,11 @@ static int megasky_mt352_frontend_attach(struct dvb_usb_adapter *adap)
}
static struct qt1010_config megasky_qt1010_config = {
- .i2c_address = 0xc4
+ .i2c_address = 0x62
};
static int megasky_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
{
- struct m9206_state *m = adap->dev->priv;
-
- m->i2c_r[M9206_I2C_TUNER].addr = megasky_qt1010_config.i2c_address;
- m->i2c_r[M9206_I2C_TUNER].magic = 0xc5;
-
if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap,
&megasky_qt1010_config) == NULL)
return -ENODEV;
@@ -419,8 +416,40 @@ static int megasky_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static struct tda1004x_config digivox_tda10046_config = {
+ .demod_address = 0x08,
+ .invert = 0,
+ .invert_oclk = 0,
+ .ts_mode = TDA10046_TS_SERIAL,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .if_freq = TDA10046_FREQ_045,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GPTRI,
+ .request_firmware = NULL,
+};
+
+static int digivox_tda10046_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ deb_rc("digivox_tda10046_frontend_attach!\n");
+
+ if ((adap->fe = dvb_attach(tda10046_attach, &digivox_tda10046_config,
+ &adap->dev->i2c_adap)) == NULL)
+ return -EIO;
+
+ return 0;
+}
+
+static int digivox_tda8275_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ if (dvb_attach(tda827x_attach, adap->fe, 0x60, &adap->dev->i2c_adap,
+ NULL) == NULL)
+ return -ENODEV;
+ return 0;
+}
+
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties megasky_properties;
+static struct dvb_usb_device_properties digivox_mini_ii_properties;
static int m920x_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -429,30 +458,36 @@ static int m920x_probe(struct usb_interface *intf,
struct usb_host_interface *alt;
int ret;
- if ((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) {
- deb_rc("probed!\n");
+ deb_rc("Probed!\n");
- alt = usb_altnum_to_altsetting(intf, 1);
- if (alt == NULL) {
- deb_rc("not alt found!\n");
- return -ENODEV;
- }
+ if (((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) ||
+ ((ret = dvb_usb_device_init(intf, &digivox_mini_ii_properties, THIS_MODULE, &d)) == 0))
+ goto found;
- ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
- alt->desc.bAlternateSetting);
- if (ret < 0)
- return ret;
-
- deb_rc("Changed to alternate setting!\n");
+ return ret;
- if ((ret = m9206_rc_init(d->udev)) != 0)
- return ret;
+found:
+ alt = usb_altnum_to_altsetting(intf, 1);
+ if (alt == NULL) {
+ deb_rc("No alt found!\n");
+ return -ENODEV;
}
+
+ ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
+ if (ret < 0)
+ return ret;
+
+ if ((ret = m9206_init(d)) != 0)
+ return ret;
+
return ret;
}
static struct usb_device_id m920x_table [] = {
{ USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) },
+ { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC,
+ USB_PID_MSI_DIGI_VOX_MINI_II) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, m920x_table);
@@ -471,7 +506,7 @@ static struct dvb_usb_device_properties megasky_properties = {
.size_of_priv = sizeof(struct m9206_state),
- .identify_state = megasky_identify_state,
+ .identify_state = m920x_identify_state,
.num_adapters = 1,
.adapter = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
@@ -502,6 +537,50 @@ static struct dvb_usb_device_properties megasky_properties = {
{ "MSI Mega Sky 580 DVB-T USB2.0",
{ &m920x_table[0], NULL },
{ NULL },
+ }
+ }
+};
+
+static struct dvb_usb_device_properties digivox_mini_ii_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .firmware = "dvb-usb-digivox-02.fw",
+ .download_firmware = m9206_firmware_download,
+
+ .size_of_priv = sizeof(struct m9206_state),
+
+ .identify_state = m920x_identify_state,
+ .num_adapters = 1,
+ .adapter = {{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 8,
+ .pid_filter = m9206_pid_filter,
+ .pid_filter_ctrl = m9206_pid_filter_ctrl,
+
+ .frontend_attach = digivox_tda10046_frontend_attach,
+ .tuner_attach = digivox_tda8275_tuner_attach,
+
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x81,
+ .u = {
+ .bulk = {
+ .buffersize = 0x4000,
+ }
+ }
+ },
+ }},
+ .i2c_algo = &m9206_i2c_algo,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "MSI DIGI VOX mini II DVB-T USB2.0",
+ { &m920x_table[1], NULL },
+ { NULL },
},
}
};
diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/dvb/dvb-usb/m920x.h
index c354196ffe5d..7dd3db65c80e 100644
--- a/drivers/media/dvb/dvb-usb/m920x.h
+++ b/drivers/media/dvb/dvb-usb/m920x.h
@@ -19,17 +19,49 @@
#define M9206_MAX_FILTERS 8
-#define M9206_I2C_TUNER 0
-#define M9206_I2C_DEMOD 1
-#define M9206_I2C_MAX 2
+/*
+sequences found in logs:
+[index value]
+0x80 write addr
+(0x00 out byte)*
+0x40 out byte
+
+0x80 write addr
+(0x00 out byte)*
+0x80 read addr
+(0x21 in byte)*
+0x60 in byte
+
+this sequence works:
+0x80 read addr
+(0x21 in byte)*
+0x60 in byte
+
+Guess at API of the I2C function:
+I2C operation is done one byte at a time with USB control messages. The
+index the messages is sent to is made up of a set of flags that control
+the I2C bus state:
+0x80: Send START condition. After a START condition, one would normally
+ always send the 7-bit slave I2C address as the 7 MSB, followed by
+ the read/write bit as the LSB.
+0x40: Send STOP condition. This should be set on the last byte of an
+ I2C transaction.
+0x20: Read a byte from the slave. As opposed to writing a byte to the
+ slave. The slave will normally not produce any data unless you
+ set the R/W bit to 1 when sending the slave's address after the
+ START condition.
+0x01: Respond with ACK, as opposed to a NACK. For a multi-byte read,
+ the master should send an ACK, that is pull SDA low during the 9th
+ clock cycle, after every byte but the last. This flags only makes
+ sense when bit 0x20 is set, indicating a read.
+
+What any other bits might mean, or how to get the slave's ACK/NACK
+response to a write, is unknown.
+*/
struct m9206_state {
u16 filters[M9206_MAX_FILTERS];
int filtering_enabled;
int rep_count;
- struct {
- unsigned char addr;
- unsigned char magic;
- }i2c_r[M9206_I2C_MAX];
};
#endif
diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c
new file mode 100644
index 000000000000..518d7ad217df
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/opera1.c
@@ -0,0 +1,581 @@
+/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card
+*
+* Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org)
+* Copyright (C) 2006 Marco Gittler (g.marco@freenet.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.
+*
+* see Documentation/dvb/README.dvb-usb for more information
+*/
+
+#include "opera1.h"
+#include "stv0299.h"
+
+#define OPERA_READ_MSG 0
+#define OPERA_WRITE_MSG 1
+#define OPERA_I2C_TUNER 0xd1
+
+#define READ_FX2_REG_REQ 0xba
+#define READ_MAC_ADDR 0x08
+#define OPERA_WRITE_FX2 0xbb
+#define OPERA_TUNER_REQ 0xb1
+#define REG_1F_SYMBOLRATE_BYTE0 0x1f
+#define REG_20_SYMBOLRATE_BYTE1 0x20
+#define REG_21_SYMBOLRATE_BYTE2 0x21
+
+#define ADDR_B600_VOLTAGE_13V (0x02)
+#define ADDR_B601_VOLTAGE_18V (0x03)
+#define ADDR_B1A6_STREAM_CTRL (0x04)
+#define ADDR_B880_READ_REMOTE (0x05)
+
+struct opera1_state {
+ u32 last_key_pressed;
+};
+struct opera_rc_keys {
+ u32 keycode;
+ u32 event;
+};
+
+int dvb_usb_opera1_debug;
+module_param_named(debug, dvb_usb_opera1_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))."
+ DVB_USB_DEBUG_STATUS);
+
+static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value,
+ u8 * data, u16 len, int flags)
+{
+ int ret;
+ u8 r;
+ u8 u8buf[len];
+
+ unsigned int pipe = (flags == OPERA_READ_MSG) ?
+ usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0);
+ u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
+
+ if (flags == OPERA_WRITE_MSG)
+ memcpy(u8buf, data, len);
+ ret =
+ usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR,
+ value, 0x0, u8buf, len, 2000);
+
+ if (request == OPERA_TUNER_REQ) {
+ if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR,
+ 0x01, 0x0, &r, 1, 2000)<1 || r!=0x08)
+ return 0;
+ }
+ if (flags == OPERA_READ_MSG)
+ memcpy(data, u8buf, len);
+ return ret;
+}
+
+/* I2C */
+
+static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr,
+ u8 * buf, u16 len)
+{
+ int ret = 0;
+ u8 request;
+ u16 value;
+
+ if (!dev) {
+ info("no usb_device");
+ return -EINVAL;
+ }
+ if (mutex_lock_interruptible(&dev->usb_mutex) < 0)
+ return -EAGAIN;
+
+ switch (addr>>1){
+ case ADDR_B600_VOLTAGE_13V:
+ request=0xb6;
+ value=0x00;
+ break;
+ case ADDR_B601_VOLTAGE_18V:
+ request=0xb6;
+ value=0x01;
+ break;
+ case ADDR_B1A6_STREAM_CTRL:
+ request=0xb1;
+ value=0xa6;
+ break;
+ case ADDR_B880_READ_REMOTE:
+ request=0xb8;
+ value=0x80;
+ break;
+ default:
+ request=0xb1;
+ value=addr;
+ }
+ ret = opera1_xilinx_rw(dev->udev, request,
+ value, buf, len,
+ addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG);
+
+ mutex_unlock(&dev->usb_mutex);
+ return ret;
+}
+
+static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int i = 0, tmp = 0;
+
+ if (!d)
+ return -ENODEV;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ if ((tmp = opera1_usb_i2c_msgxfer(d,
+ (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0),
+ msg[i].buf,
+ msg[i].len
+ )!= msg[i].len)) {
+ break;
+ }
+ if (dvb_usb_opera1_debug & 0x10)
+ info("sending i2c mesage %d %d", tmp, msg[i].len);
+ }
+ mutex_unlock(&d->i2c_mutex);
+ return num;
+}
+
+static u32 opera1_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm opera1_i2c_algo = {
+ .master_xfer = opera1_i2c_xfer,
+ .functionality = opera1_i2c_func,
+};
+
+static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ static u8 command_13v[1]={0x00};
+ static u8 command_18v[1]={0x01};
+ struct i2c_msg msg[] = {
+ {.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1},
+ };
+ struct dvb_usb_adapter *udev_adap =
+ (struct dvb_usb_adapter *)(fe->dvb->priv);
+ if (voltage == SEC_VOLTAGE_18) {
+ msg[0].addr = ADDR_B601_VOLTAGE_18V;
+ msg[0].buf = command_18v;
+ }
+ i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1);
+ return 0;
+}
+
+static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate,
+ u32 ratio)
+{
+ stv0299_writereg(fe, 0x13, 0x98);
+ stv0299_writereg(fe, 0x14, 0x95);
+ stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0);
+ return 0;
+
+}
+static u8 opera1_inittab[] = {
+ 0x00, 0xa1,
+ 0x01, 0x15,
+ 0x02, 0x00,
+ 0x03, 0x00,
+ 0x04, 0x7d,
+ 0x05, 0x05,
+ 0x06, 0x02,
+ 0x07, 0x00,
+ 0x0b, 0x00,
+ 0x0c, 0x01,
+ 0x0d, 0x81,
+ 0x0e, 0x44,
+ 0x0f, 0x19,
+ 0x10, 0x3f,
+ 0x11, 0x84,
+ 0x12, 0xda,
+ 0x13, 0x98,
+ 0x14, 0x95,
+ 0x15, 0xc9,
+ 0x16, 0xeb,
+ 0x17, 0x00,
+ 0x18, 0x19,
+ 0x19, 0x8b,
+ 0x1a, 0x00,
+ 0x1b, 0x82,
+ 0x1c, 0x7f,
+ 0x1d, 0x00,
+ 0x1e, 0x00,
+ REG_1F_SYMBOLRATE_BYTE0, 0x06,
+ REG_20_SYMBOLRATE_BYTE1, 0x50,
+ REG_21_SYMBOLRATE_BYTE2, 0x10,
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x24, 0x37,
+ 0x25, 0xbc,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x00,
+ 0x29, 0x1e,
+ 0x2a, 0x14,
+ 0x2b, 0x1f,
+ 0x2c, 0x09,
+ 0x2d, 0x0a,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x00,
+ 0x31, 0x1f,
+ 0x32, 0x19,
+ 0x33, 0xfc,
+ 0x34, 0x13,
+ 0xff, 0xff,
+};
+
+static struct stv0299_config opera1_stv0299_config = {
+ .demod_address = 0xd0>>1,
+ .min_delay_ms = 100,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .skip_reinit = 0,
+ .lock_output = STV0229_LOCKOUTPUT_0,
+ .volt13_op0_op1 = STV0299_VOLT13_OP0,
+ .inittab = opera1_inittab,
+ .set_symbol_rate = opera1_stv0299_set_symbol_rate,
+};
+
+static int opera1_frontend_attach(struct dvb_usb_adapter *d)
+{
+ if ((d->fe =
+ dvb_attach(stv0299_attach, &opera1_stv0299_config,
+ &d->dev->i2c_adap)) != NULL) {
+ d->fe->ops.set_voltage = opera1_set_voltage;
+ return 0;
+ }
+ info("not attached stv0299");
+ return -EIO;
+}
+
+static int opera1_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ dvb_attach(
+ dvb_pll_attach, adap->fe, 0xc0>>1,
+ &adap->dev->i2c_adap, &dvb_pll_opera1
+ );
+ return 0;
+}
+
+static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ u8 val = onoff ? 0x01 : 0x00;
+
+ if (dvb_usb_opera1_debug)
+ info("power %s", onoff ? "on" : "off");
+ return opera1_xilinx_rw(d->udev, 0xb7, val,
+ &val, 1, OPERA_WRITE_MSG);
+}
+
+static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ static u8 buf_start[2] = { 0xff, 0x03 };
+ static u8 buf_stop[2] = { 0xff, 0x00 };
+ struct i2c_msg start_tuner[] = {
+ {.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2},
+ };
+ if (dvb_usb_opera1_debug)
+ info("streaming %s", onoff ? "on" : "off");
+ i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1);
+ return 0;
+}
+
+static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+ int onoff)
+{
+ u8 b_pid[3];
+ struct i2c_msg msg[] = {
+ {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3},
+ };
+ if (dvb_usb_opera1_debug)
+ info("pidfilter index: %d pid: %d %s", index, pid,
+ onoff ? "on" : "off");
+ b_pid[0] = (2 * index) + 4;
+ b_pid[1] = onoff ? (pid & 0xff) : (0x00);
+ b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00);
+ i2c_transfer(&adap->dev->i2c_adap, msg, 1);
+ return 0;
+}
+
+static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff)
+{
+ int u = 0x04;
+ u8 b_pid[3];
+ struct i2c_msg msg[] = {
+ {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3},
+ };
+ if (dvb_usb_opera1_debug)
+ info("%s hw-pidfilter", onoff ? "enable" : "disable");
+ for (; u < 0x7e; u += 2) {
+ b_pid[0] = u;
+ b_pid[1] = 0;
+ b_pid[2] = 0x80;
+ i2c_transfer(&adap->dev->i2c_adap, msg, 1);
+ }
+ return 0;
+}
+
+static struct dvb_usb_rc_key opera1_rc_keys[] = {
+ {0x5f, 0xa0, KEY_1},
+ {0x51, 0xaf, KEY_2},
+ {0x5d, 0xa2, KEY_3},
+ {0x41, 0xbe, KEY_4},
+ {0x0b, 0xf5, KEY_5},
+ {0x43, 0xbd, KEY_6},
+ {0x47, 0xb8, KEY_7},
+ {0x49, 0xb6, KEY_8},
+ {0x05, 0xfa, KEY_9},
+ {0x45, 0xba, KEY_0},
+ {0x09, 0xf6, KEY_UP}, /*chanup */
+ {0x1b, 0xe5, KEY_DOWN}, /*chandown */
+ {0x5d, 0xa3, KEY_LEFT}, /*voldown */
+ {0x5f, 0xa1, KEY_RIGHT}, /*volup */
+ {0x07, 0xf8, KEY_SPACE}, /*tab */
+ {0x1f, 0xe1, KEY_ENTER}, /*play ok */
+ {0x1b, 0xe4, KEY_Z}, /*zoom */
+ {0x59, 0xa6, KEY_M}, /*mute */
+ {0x5b, 0xa5, KEY_F}, /*tv/f */
+ {0x19, 0xe7, KEY_R}, /*rec */
+ {0x01, 0xfe, KEY_S}, /*Stop */
+ {0x03, 0xfd, KEY_P}, /*pause */
+ {0x03, 0xfc, KEY_W}, /*<- -> */
+ {0x07, 0xf9, KEY_C}, /*capture */
+ {0x47, 0xb9, KEY_Q}, /*exit */
+ {0x43, 0xbc, KEY_O}, /*power */
+
+};
+
+static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state)
+{
+ struct opera1_state *opst = dev->priv;
+ u8 rcbuffer[32];
+ const u16 startmarker1 = 0x10ed;
+ const u16 startmarker2 = 0x11ec;
+ struct i2c_msg read_remote[] = {
+ {.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32},
+ };
+ int i = 0;
+ u32 send_key = 0;
+
+ if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) {
+ for (i = 0; i < 32; i++) {
+ if (rcbuffer[i])
+ send_key |= 1;
+ if (i < 31)
+ send_key = send_key << 1;
+ }
+ if (send_key & 0x8000)
+ send_key = (send_key << 1) | (send_key >> 15 & 0x01);
+
+ if (send_key == 0xffff && opst->last_key_pressed != 0) {
+ *state = REMOTE_KEY_REPEAT;
+ *event = opst->last_key_pressed;
+ return 0;
+ }
+ for (; send_key != 0;) {
+ if (send_key >> 16 == startmarker2) {
+ break;
+ } else if (send_key >> 16 == startmarker1) {
+ send_key =
+ (send_key & 0xfffeffff) | (startmarker1 << 16);
+ break;
+ } else
+ send_key >>= 1;
+ }
+
+ if (send_key == 0)
+ return 0;
+
+ send_key = (send_key & 0xffff) | 0x0100;
+
+ for (i = 0; i < ARRAY_SIZE(opera1_rc_keys); i++) {
+ if ((opera1_rc_keys[i].custom * 256 +
+ opera1_rc_keys[i].data) == (send_key & 0xffff)) {
+ *state = REMOTE_KEY_PRESSED;
+ *event = opera1_rc_keys[i].event;
+ opst->last_key_pressed =
+ opera1_rc_keys[i].event;
+ break;
+ }
+ opst->last_key_pressed = 0;
+ }
+ } else
+ *state = REMOTE_NO_KEY_PRESSED;
+ return 0;
+}
+
+static struct usb_device_id opera1_table[] = {
+ {USB_DEVICE(USB_VID_CYPRESS, USB_PID_OPERA1_COLD)},
+ {USB_DEVICE(USB_VID_OPERA1, USB_PID_OPERA1_WARM)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, opera1_table);
+
+static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ u8 command[] = { READ_MAC_ADDR };
+ opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG);
+ opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG);
+ return 0;
+}
+static int opera1_xilinx_load_firmware(struct usb_device *dev,
+ const char *filename)
+{
+ const struct firmware *fw = NULL;
+ u8 *b, *p;
+ int ret = 0, i;
+ u8 testval;
+ info("start downloading fpga firmware");
+
+ if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) {
+ err("did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+ filename);
+ return ret;
+ } else {
+ p = kmalloc(fw->size, GFP_KERNEL);
+ opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG);
+ if (p != NULL && testval != 0x67) {
+
+ u8 reset = 0, fpga_command = 0;
+ memcpy(p, fw->data, fw->size);
+ /* clear fpga ? */
+ opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1,
+ OPERA_WRITE_MSG);
+ for (i = 0; p[i] != 0 && i < fw->size;) {
+ b = (u8 *) p + i;
+ if (opera1_xilinx_rw
+ (dev, OPERA_WRITE_FX2, 0x0, b + 1, b[0],
+ OPERA_WRITE_MSG) != b[0]
+ ) {
+ err("error while transferring firmware");
+ ret = -EINVAL;
+ break;
+ }
+ i = i + 1 + b[0];
+ }
+ /* restart the CPU */
+ if (ret || opera1_xilinx_rw
+ (dev, 0xa0, 0xe600, &reset, 1,
+ OPERA_WRITE_MSG) != 1) {
+ err("could not restart the USB controller CPU.");
+ ret = -EINVAL;
+ }
+ kfree(p);
+ }
+ }
+ if (fw) {
+ release_firmware(fw);
+ }
+ return ret;
+}
+
+static struct dvb_usb_device_properties opera1_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-opera-01.fw",
+ .size_of_priv = sizeof(struct opera1_state),
+
+ .power_ctrl = opera1_power_ctrl,
+ .i2c_algo = &opera1_i2c_algo,
+
+ .rc_key_map = opera1_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(opera1_rc_keys),
+ .rc_interval = 200,
+ .rc_query = opera1_rc_query,
+ .read_mac_address = opera1_read_mac_address,
+ .generic_bulk_ctrl_endpoint = 0x00,
+ /* parameter for the MPEG2-data transfer */
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .frontend_attach = opera1_frontend_attach,
+ .streaming_ctrl = opera1_streaming_ctrl,
+ .tuner_attach = opera1_tuner_attach,
+ .caps =
+ DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter = opera1_pid_filter,
+ .pid_filter_ctrl = opera1_pid_filter_control,
+ .pid_filter_count = 252,
+ .stream = {
+ .type = USB_BULK,
+ .count = 10,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ }
+ },
+ .num_device_descs = 1,
+ .devices = {
+ {"Opera1 DVB-S USB2.0",
+ {&opera1_table[0], NULL},
+ {&opera1_table[1], NULL},
+ },
+ }
+};
+
+static int opera1_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct dvb_usb_device *d;
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM &&
+ udev->descriptor.idVendor == USB_VID_OPERA1 &&
+ (d == NULL
+ || opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga.fw") != 0)
+ ) {
+ return -EINVAL;
+ }
+
+ if (dvb_usb_device_init(intf, &opera1_properties, THIS_MODULE, &d) != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static struct usb_driver opera1_driver = {
+ .name = "opera1",
+ .probe = opera1_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = opera1_table,
+};
+
+static int __init opera1_module_init(void)
+{
+ int result = 0;
+ if ((result = usb_register(&opera1_driver))) {
+ err("usb_register failed. Error number %d", result);
+ }
+ return result;
+}
+
+static void __exit opera1_module_exit(void)
+{
+ usb_deregister(&opera1_driver);
+}
+
+module_init(opera1_module_init);
+module_exit(opera1_module_exit);
+
+MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org");
+MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de");
+MODULE_DESCRIPTION("Driver for Opera1 DVB-S device");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/opera1.h b/drivers/media/dvb/dvb-usb/opera1.h
new file mode 100644
index 000000000000..53174427902d
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/opera1.h
@@ -0,0 +1,9 @@
+#ifndef _OPERA1_H_
+#define _OPERA1_H_
+
+#define DVB_USB_LOG_PREFIX "opera"
+#include "dvb-usb.h"
+
+extern int dvb_usb_opera1_debug;
+#define deb_xfer(args...) dprintk(dvb_usb_opera1_debug,0x02,args)
+#endif
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c
index 95d29976ed78..88dc4367a2e3 100644
--- a/drivers/media/dvb/dvb-usb/ttusb2.c
+++ b/drivers/media/dvb/dvb-usb/ttusb2.c
@@ -184,6 +184,7 @@ static int ttusb2_probe(struct usb_interface *intf,
static struct usb_device_id ttusb2_table [] = {
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) },
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, ttusb2_table);
@@ -227,12 +228,16 @@ static struct dvb_usb_device_properties ttusb2_properties = {
.generic_bulk_ctrl_endpoint = 0x01,
- .num_device_descs = 1,
+ .num_device_descs = 2,
.devices = {
{ "Pinnacle 400e DVB-S USB2.0",
{ &ttusb2_table[0], NULL },
{ NULL },
},
+ { "Pinnacle 450e DVB-S USB2.0",
+ { &ttusb2_table[1], NULL },
+ { NULL },
+ },
}
};
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 22c2cf2cea98..ff448761dcef 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -205,6 +205,13 @@ config DVB_TDA10021
help
A DVB-C tuner module. Say Y when you want to support this frontend.
+config DVB_TDA10023
+ tristate "Philips TDA10023 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
@@ -280,8 +287,12 @@ comment "Tuners/PLL support"
depends on DVB_CORE
config DVB_PLL
- tristate
+ tristate "Generic I2C PLL based tuners"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ This module driver a number of tuners based on PLL chips with a
+ common I2C interface. Say Y when you want to support these tuners.
config DVB_TDA826X
tristate "Philips TDA826X silicon tuner"
@@ -290,6 +301,13 @@ config DVB_TDA826X
help
A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+config DVB_TDA827X
+ tristate "Philips TDA827X silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T silicon tuner module. Say Y when you want to support this tuner.
+
config DVB_TUNER_QT1010
tristate "Quantek QT1010 silicon tuner"
depends on DVB_CORE && I2C
@@ -304,14 +322,6 @@ config DVB_TUNER_MT2060
help
A driver for the silicon IF tuner MT2060 from Microtune.
-config DVB_TUNER_LGH06XF
- tristate "LG TDVS-H06xF ATSC tuner"
- depends on DVB_CORE && I2C
- select DVB_PLL
- default m if DVB_FE_CUSTOMISE
- help
- A driver for the LG TDVS-H06xF ATSC tuner family.
-
comment "Miscellaneous devices"
depends on DVB_CORE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index a646d9969b71..27f386585d43 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_DVB_MT352) += mt352.o
obj-$(CONFIG_DVB_ZL10353) += zl10353.o
obj-$(CONFIG_DVB_CX22702) += cx22702.o
obj-$(CONFIG_DVB_TDA10021) += tda10021.o
+obj-$(CONFIG_DVB_TDA10023) += tda10023.o
obj-$(CONFIG_DVB_STV0297) += stv0297.o
obj-$(CONFIG_DVB_NXT200X) += nxt200x.o
obj-$(CONFIG_DVB_OR51211) += or51211.o
@@ -37,7 +38,7 @@ 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_TDA827X) += tda827x.o
obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
obj-$(CONFIG_DVB_TUA6100) += tua6100.o
-obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index 62de760c844f..5f96ffda91ad 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -27,17 +27,29 @@
/* ----------------------------------------------------------- */
/* descriptions */
+/* Set AGC TOP value to 103 dBuV:
+ 0x80 = Control Byte
+ 0x40 = 250 uA charge pump (irrelevant)
+ 0x18 = Aux Byte to follow
+ 0x06 = 64.5 kHz divider (irrelevant)
+ 0x01 = Disable Vt (aka sleep)
+
+ 0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA)
+ 0x50 = AGC Take over point = 103 dBuV */
+static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 };
+
struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
.name = "Thomson dtt7579",
.min = 177000000,
.max = 858000000,
- .count = 5,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0xb4, 0x03 },
+ .count = 4,
.entries = {
- { 0, 36166667, 166666, 0xb4, 0x03 }, /* go sleep */
- { 443250000, 36166667, 166666, 0xb4, 0x02 },
- { 542000000, 36166667, 166666, 0xb4, 0x08 },
- { 771000000, 36166667, 166666, 0xbc, 0x08 },
- { 999999999, 36166667, 166666, 0xf4, 0x08 },
+ { 443250000, 166667, 0xb4, 0x02 },
+ { 542000000, 166667, 0xb4, 0x08 },
+ { 771000000, 166667, 0xbc, 0x08 },
+ { 999999999, 166667, 0xf4, 0x08 },
},
};
EXPORT_SYMBOL(dvb_pll_thomson_dtt7579);
@@ -46,11 +58,12 @@ struct dvb_pll_desc dvb_pll_thomson_dtt7610 = {
.name = "Thomson dtt7610",
.min = 44000000,
.max = 958000000,
+ .iffreq= 44000000,
.count = 3,
.entries = {
- { 157250000, 44000000, 62500, 0x8e, 0x39 },
- { 454000000, 44000000, 62500, 0x8e, 0x3a },
- { 999999999, 44000000, 62500, 0x8e, 0x3c },
+ { 157250000, 62500, 0x8e, 0x39 },
+ { 454000000, 62500, 0x8e, 0x3a },
+ { 999999999, 62500, 0x8e, 0x3c },
},
};
EXPORT_SYMBOL(dvb_pll_thomson_dtt7610);
@@ -66,14 +79,15 @@ struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
.min = 177000000,
.max = 896000000,
.setbw = thomson_dtt759x_bw,
- .count = 6,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0x84, 0x03 },
+ .count = 5,
.entries = {
- { 0, 36166667, 166666, 0x84, 0x03 },
- { 264000000, 36166667, 166666, 0xb4, 0x02 },
- { 470000000, 36166667, 166666, 0xbc, 0x02 },
- { 735000000, 36166667, 166666, 0xbc, 0x08 },
- { 835000000, 36166667, 166666, 0xf4, 0x08 },
- { 999999999, 36166667, 166666, 0xfc, 0x08 },
+ { 264000000, 166667, 0xb4, 0x02 },
+ { 470000000, 166667, 0xbc, 0x02 },
+ { 735000000, 166667, 0xbc, 0x08 },
+ { 835000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x08 },
},
};
EXPORT_SYMBOL(dvb_pll_thomson_dtt759x);
@@ -82,14 +96,15 @@ struct dvb_pll_desc dvb_pll_lg_z201 = {
.name = "LG z201",
.min = 174000000,
.max = 862000000,
- .count = 6,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0xbc, 0x03 },
+ .count = 5,
.entries = {
- { 0, 36166667, 166666, 0xbc, 0x03 },
- { 157500000, 36166667, 166666, 0xbc, 0x01 },
- { 443250000, 36166667, 166666, 0xbc, 0x02 },
- { 542000000, 36166667, 166666, 0xbc, 0x04 },
- { 830000000, 36166667, 166666, 0xf4, 0x04 },
- { 999999999, 36166667, 166666, 0xfc, 0x04 },
+ { 157500000, 166667, 0xbc, 0x01 },
+ { 443250000, 166667, 0xbc, 0x02 },
+ { 542000000, 166667, 0xbc, 0x04 },
+ { 830000000, 166667, 0xf4, 0x04 },
+ { 999999999, 166667, 0xfc, 0x04 },
},
};
EXPORT_SYMBOL(dvb_pll_lg_z201);
@@ -98,11 +113,12 @@ struct dvb_pll_desc dvb_pll_microtune_4042 = {
.name = "Microtune 4042 FI5",
.min = 57000000,
.max = 858000000,
+ .iffreq= 44000000,
.count = 3,
.entries = {
- { 162000000, 44000000, 62500, 0x8e, 0xa1 },
- { 457000000, 44000000, 62500, 0x8e, 0x91 },
- { 999999999, 44000000, 62500, 0x8e, 0x31 },
+ { 162000000, 62500, 0x8e, 0xa1 },
+ { 457000000, 62500, 0x8e, 0x91 },
+ { 999999999, 62500, 0x8e, 0x31 },
},
};
EXPORT_SYMBOL(dvb_pll_microtune_4042);
@@ -112,11 +128,13 @@ struct dvb_pll_desc dvb_pll_thomson_dtt761x = {
.name = "Thomson dtt761x",
.min = 57000000,
.max = 863000000,
+ .iffreq= 44000000,
.count = 3,
+ .initdata = tua603x_agc103,
.entries = {
- { 147000000, 44000000, 62500, 0x8e, 0x39 },
- { 417000000, 44000000, 62500, 0x8e, 0x3a },
- { 999999999, 44000000, 62500, 0x8e, 0x3c },
+ { 147000000, 62500, 0x8e, 0x39 },
+ { 417000000, 62500, 0x8e, 0x3a },
+ { 999999999, 62500, 0x8e, 0x3c },
},
};
EXPORT_SYMBOL(dvb_pll_thomson_dtt761x);
@@ -125,17 +143,18 @@ struct dvb_pll_desc dvb_pll_unknown_1 = {
.name = "unknown 1", /* used by dntv live dvb-t */
.min = 174000000,
.max = 862000000,
+ .iffreq= 36166667,
.count = 9,
.entries = {
- { 150000000, 36166667, 166666, 0xb4, 0x01 },
- { 173000000, 36166667, 166666, 0xbc, 0x01 },
- { 250000000, 36166667, 166666, 0xb4, 0x02 },
- { 400000000, 36166667, 166666, 0xbc, 0x02 },
- { 420000000, 36166667, 166666, 0xf4, 0x02 },
- { 470000000, 36166667, 166666, 0xfc, 0x02 },
- { 600000000, 36166667, 166666, 0xbc, 0x08 },
- { 730000000, 36166667, 166666, 0xf4, 0x08 },
- { 999999999, 36166667, 166666, 0xfc, 0x08 },
+ { 150000000, 166667, 0xb4, 0x01 },
+ { 173000000, 166667, 0xbc, 0x01 },
+ { 250000000, 166667, 0xb4, 0x02 },
+ { 400000000, 166667, 0xbc, 0x02 },
+ { 420000000, 166667, 0xf4, 0x02 },
+ { 470000000, 166667, 0xfc, 0x02 },
+ { 600000000, 166667, 0xbc, 0x08 },
+ { 730000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x08 },
},
};
EXPORT_SYMBOL(dvb_pll_unknown_1);
@@ -147,11 +166,12 @@ struct dvb_pll_desc dvb_pll_tua6010xs = {
.name = "Infineon TUA6010XS",
.min = 44250000,
.max = 858000000,
+ .iffreq= 36125000,
.count = 3,
.entries = {
- { 115750000, 36125000, 62500, 0x8e, 0x03 },
- { 403250000, 36125000, 62500, 0x8e, 0x06 },
- { 999999999, 36125000, 62500, 0x8e, 0x85 },
+ { 115750000, 62500, 0x8e, 0x03 },
+ { 403250000, 62500, 0x8e, 0x06 },
+ { 999999999, 62500, 0x8e, 0x85 },
},
};
EXPORT_SYMBOL(dvb_pll_tua6010xs);
@@ -161,12 +181,13 @@ struct dvb_pll_desc dvb_pll_env57h1xd5 = {
.name = "Panasonic ENV57H1XD5",
.min = 44250000,
.max = 858000000,
+ .iffreq= 36125000,
.count = 4,
.entries = {
- { 153000000, 36291666, 166666, 0xc2, 0x41 },
- { 470000000, 36291666, 166666, 0xc2, 0x42 },
- { 526000000, 36291666, 166666, 0xc2, 0x84 },
- { 999999999, 36291666, 166666, 0xc2, 0xa4 },
+ { 153000000, 166667, 0xc2, 0x41 },
+ { 470000000, 166667, 0xc2, 0x42 },
+ { 526000000, 166667, 0xc2, 0x84 },
+ { 999999999, 166667, 0xc2, 0xa4 },
},
};
EXPORT_SYMBOL(dvb_pll_env57h1xd5);
@@ -185,20 +206,21 @@ struct dvb_pll_desc dvb_pll_tda665x = {
.min = 44250000,
.max = 858000000,
.setbw = tda665x_bw,
+ .iffreq= 36166667,
.count = 12,
.entries = {
- { 93834000, 36249333, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ },
- { 123834000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
- { 161000000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
- { 163834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
- { 253834000, 36249333, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ },
- { 383834000, 36249333, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ },
- { 443834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
- { 444000000, 36249333, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
- { 583834000, 36249333, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ },
- { 793834000, 36249333, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ },
- { 444834000, 36249333, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
- { 861000000, 36249333, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ },
+ { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ },
+ { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
+ { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
+ { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
+ { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ },
+ { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ },
+ { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
+ { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
+ { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ },
+ { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ },
+ { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
+ { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ },
}
};
EXPORT_SYMBOL(dvb_pll_tda665x);
@@ -216,12 +238,13 @@ struct dvb_pll_desc dvb_pll_tua6034 = {
.name = "Infineon TUA6034",
.min = 44250000,
.max = 858000000,
+ .iffreq= 36166667,
.count = 3,
.setbw = tua6034_bw,
.entries = {
- { 174500000, 36166667, 62500, 0xce, 0x01 },
- { 230000000, 36166667, 62500, 0xce, 0x02 },
- { 999999999, 36166667, 62500, 0xce, 0x04 },
+ { 174500000, 62500, 0xce, 0x01 },
+ { 230000000, 62500, 0xce, 0x02 },
+ { 999999999, 62500, 0xce, 0x04 },
},
};
EXPORT_SYMBOL(dvb_pll_tua6034);
@@ -233,11 +256,13 @@ struct dvb_pll_desc dvb_pll_lg_tdvs_h06xf = {
.name = "LG TDVS-H06xF",
.min = 54000000,
.max = 863000000,
+ .iffreq= 44000000,
+ .initdata = tua603x_agc103,
.count = 3,
.entries = {
- { 165000000, 44000000, 62500, 0xce, 0x01 },
- { 450000000, 44000000, 62500, 0xce, 0x02 },
- { 999999999, 44000000, 62500, 0xce, 0x04 },
+ { 165000000, 62500, 0xce, 0x01 },
+ { 450000000, 62500, 0xce, 0x02 },
+ { 999999999, 62500, 0xce, 0x04 },
},
};
EXPORT_SYMBOL(dvb_pll_lg_tdvs_h06xf);
@@ -255,16 +280,17 @@ struct dvb_pll_desc dvb_pll_fmd1216me = {
.name = "Philips FMD1216ME",
.min = 50870000,
.max = 858000000,
+ .iffreq= 36125000,
.setbw = fmd1216me_bw,
.count = 7,
.entries = {
- { 143870000, 36213333, 166667, 0xbc, 0x41 },
- { 158870000, 36213333, 166667, 0xf4, 0x41 },
- { 329870000, 36213333, 166667, 0xbc, 0x42 },
- { 441870000, 36213333, 166667, 0xf4, 0x42 },
- { 625870000, 36213333, 166667, 0xbc, 0x44 },
- { 803870000, 36213333, 166667, 0xf4, 0x44 },
- { 999999999, 36213333, 166667, 0xfc, 0x44 },
+ { 143870000, 166667, 0xbc, 0x41 },
+ { 158870000, 166667, 0xf4, 0x41 },
+ { 329870000, 166667, 0xbc, 0x42 },
+ { 441870000, 166667, 0xf4, 0x42 },
+ { 625870000, 166667, 0xbc, 0x44 },
+ { 803870000, 166667, 0xf4, 0x44 },
+ { 999999999, 166667, 0xfc, 0x44 },
}
};
EXPORT_SYMBOL(dvb_pll_fmd1216me);
@@ -282,13 +308,14 @@ struct dvb_pll_desc dvb_pll_tded4 = {
.name = "ALPS TDED4",
.min = 47000000,
.max = 863000000,
+ .iffreq= 36166667,
.setbw = tded4_bw,
.count = 4,
.entries = {
- { 153000000, 36166667, 166667, 0x85, 0x01 },
- { 470000000, 36166667, 166667, 0x85, 0x02 },
- { 823000000, 36166667, 166667, 0x85, 0x08 },
- { 999999999, 36166667, 166667, 0x85, 0x88 },
+ { 153000000, 166667, 0x85, 0x01 },
+ { 470000000, 166667, 0x85, 0x02 },
+ { 823000000, 166667, 0x85, 0x08 },
+ { 999999999, 166667, 0x85, 0x88 },
}
};
EXPORT_SYMBOL(dvb_pll_tded4);
@@ -300,12 +327,13 @@ struct dvb_pll_desc dvb_pll_tdhu2 = {
.name = "ALPS TDHU2",
.min = 54000000,
.max = 864000000,
+ .iffreq= 44000000,
.count = 4,
.entries = {
- { 162000000, 44000000, 62500, 0x85, 0x01 },
- { 426000000, 44000000, 62500, 0x85, 0x02 },
- { 782000000, 44000000, 62500, 0x85, 0x08 },
- { 999999999, 44000000, 62500, 0x85, 0x88 },
+ { 162000000, 62500, 0x85, 0x01 },
+ { 426000000, 62500, 0x85, 0x02 },
+ { 782000000, 62500, 0x85, 0x08 },
+ { 999999999, 62500, 0x85, 0x88 },
}
};
EXPORT_SYMBOL(dvb_pll_tdhu2);
@@ -317,11 +345,12 @@ struct dvb_pll_desc dvb_pll_tuv1236d = {
.name = "Philips TUV1236D",
.min = 54000000,
.max = 864000000,
+ .iffreq= 44000000,
.count = 3,
.entries = {
- { 157250000, 44000000, 62500, 0xc6, 0x41 },
- { 454000000, 44000000, 62500, 0xc6, 0x42 },
- { 999999999, 44000000, 62500, 0xc6, 0x44 },
+ { 157250000, 62500, 0xc6, 0x41 },
+ { 454000000, 62500, 0xc6, 0x42 },
+ { 999999999, 62500, 0xc6, 0x44 },
},
};
EXPORT_SYMBOL(dvb_pll_tuv1236d);
@@ -333,14 +362,15 @@ struct dvb_pll_desc dvb_pll_samsung_tbmv = {
.name = "Samsung TBMV30111IN / TBMV30712IN1",
.min = 54000000,
.max = 860000000,
+ .iffreq= 44000000,
.count = 6,
.entries = {
- { 172000000, 44000000, 166666, 0xb4, 0x01 },
- { 214000000, 44000000, 166666, 0xb4, 0x02 },
- { 467000000, 44000000, 166666, 0xbc, 0x02 },
- { 721000000, 44000000, 166666, 0xbc, 0x08 },
- { 841000000, 44000000, 166666, 0xf4, 0x08 },
- { 999999999, 44000000, 166666, 0xfc, 0x02 },
+ { 172000000, 166667, 0xb4, 0x01 },
+ { 214000000, 166667, 0xb4, 0x02 },
+ { 467000000, 166667, 0xbc, 0x02 },
+ { 721000000, 166667, 0xbc, 0x08 },
+ { 841000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x02 },
}
};
EXPORT_SYMBOL(dvb_pll_samsung_tbmv);
@@ -352,12 +382,13 @@ struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
.name = "Philips SD1878",
.min = 950000,
.max = 2150000,
+ .iffreq= 249, /* zero-IF, offset 249 is to round up */
.count = 4,
.entries = {
- { 1250000, 499, 500, 0xc4, 0x00},
- { 1550000, 499, 500, 0xc4, 0x40},
- { 2050000, 499, 500, 0xc4, 0x80},
- { 2150000, 499, 500, 0xc4, 0xc0},
+ { 1250000, 500, 0xc4, 0x00},
+ { 1550000, 500, 0xc4, 0x40},
+ { 2050000, 500, 0xc4, 0x80},
+ { 2150000, 500, 0xc4, 0xc0},
},
};
EXPORT_SYMBOL(dvb_pll_philips_sd1878_tda8261);
@@ -388,18 +419,19 @@ struct dvb_pll_desc dvb_pll_philips_td1316 = {
.name = "Philips TD1316",
.min = 87000000,
.max = 895000000,
+ .iffreq= 36166667,
.setbw = td1316_bw,
.count = 9,
.entries = {
- { 93834000, 36166000, 166666, 0xca, 0x60},
- { 123834000, 36166000, 166666, 0xca, 0xa0},
- { 163834000, 36166000, 166666, 0xca, 0xc0},
- { 253834000, 36166000, 166666, 0xca, 0x60},
- { 383834000, 36166000, 166666, 0xca, 0xa0},
- { 443834000, 36166000, 166666, 0xca, 0xc0},
- { 583834000, 36166000, 166666, 0xca, 0x60},
- { 793834000, 36166000, 166666, 0xca, 0xa0},
- { 858834000, 36166000, 166666, 0xca, 0xe0},
+ { 93834000, 166667, 0xca, 0x60},
+ { 123834000, 166667, 0xca, 0xa0},
+ { 163834000, 166667, 0xca, 0xc0},
+ { 253834000, 166667, 0xca, 0x60},
+ { 383834000, 166667, 0xca, 0xa0},
+ { 443834000, 166667, 0xca, 0xc0},
+ { 583834000, 166667, 0xca, 0x60},
+ { 793834000, 166667, 0xca, 0xa0},
+ { 858834000, 166667, 0xca, 0xe0},
},
};
EXPORT_SYMBOL(dvb_pll_philips_td1316);
@@ -409,15 +441,41 @@ struct dvb_pll_desc dvb_pll_thomson_fe6600 = {
.name = "Thomson FE6600",
.min = 44250000,
.max = 858000000,
+ .iffreq= 36125000,
.count = 4,
.entries = {
- { 250000000, 36213333, 166667, 0xb4, 0x12 },
- { 455000000, 36213333, 166667, 0xfe, 0x11 },
- { 775500000, 36213333, 166667, 0xbc, 0x18 },
- { 999999999, 36213333, 166667, 0xf4, 0x18 },
+ { 250000000, 166667, 0xb4, 0x12 },
+ { 455000000, 166667, 0xfe, 0x11 },
+ { 775500000, 166667, 0xbc, 0x18 },
+ { 999999999, 166667, 0xf4, 0x18 },
}
};
EXPORT_SYMBOL(dvb_pll_thomson_fe6600);
+static void opera1_bw(u8 *buf, u32 freq, int bandwidth)
+{
+ if (bandwidth == BANDWIDTH_8_MHZ)
+ buf[2] |= 0x08;
+}
+
+struct dvb_pll_desc dvb_pll_opera1 = {
+ .name = "Opera Tuner",
+ .min = 900000,
+ .max = 2250000,
+ .iffreq= 0,
+ .setbw = opera1_bw,
+ .count = 8,
+ .entries = {
+ { 1064000, 500, 0xe5, 0xc6 },
+ { 1169000, 500, 0xe5, 0xe6 },
+ { 1299000, 500, 0xe5, 0x24 },
+ { 1444000, 500, 0xe5, 0x44 },
+ { 1606000, 500, 0xe5, 0x64 },
+ { 1777000, 500, 0xe5, 0x84 },
+ { 1941000, 500, 0xe5, 0xa4 },
+ { 2250000, 500, 0xe5, 0xc4 },
+ }
+};
+EXPORT_SYMBOL(dvb_pll_opera1);
struct dvb_pll_priv {
/* i2c details */
@@ -459,7 +517,8 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
if (i == desc->count)
return -EINVAL;
- div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize;
+ div = (freq + desc->iffreq + desc->entries[i].stepsize/2) /
+ desc->entries[i].stepsize;
buf[0] = div >> 8;
buf[1] = div & 0xff;
buf[2] = desc->entries[i].config;
@@ -473,7 +532,7 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
desc->name, div, buf[0], buf[1], buf[2], buf[3]);
// calculate the frequency we set it to
- return (div * desc->entries[i].stepsize) - desc->entries[i].offset;
+ return (div * desc->entries[i].stepsize) - desc->iffreq;
}
EXPORT_SYMBOL(dvb_pll_configure);
@@ -487,35 +546,27 @@ static int dvb_pll_release(struct dvb_frontend *fe)
static int dvb_pll_sleep(struct dvb_frontend *fe)
{
struct dvb_pll_priv *priv = fe->tuner_priv;
- u8 buf[4];
- struct i2c_msg msg =
- { .addr = priv->pll_i2c_address, .flags = 0,
- .buf = buf, .len = sizeof(buf) };
- 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;
- }
- if (i == priv->pll_desc->count)
- return 0;
+ if (priv->pll_desc->sleepdata) {
+ struct i2c_msg msg = { .flags = 0,
+ .addr = priv->pll_i2c_address,
+ .buf = priv->pll_desc->sleepdata + 1,
+ .len = priv->pll_desc->sleepdata[0] };
- buf[0] = 0;
- buf[1] = 0;
- buf[2] = priv->pll_desc->entries[i].config;
- buf[3] = priv->pll_desc->entries[i].cb;
+ int result;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
- return result;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ return result;
+ }
+ return 0;
}
-
- return 0;
+ /* Shouldn't be called when initdata is NULL, maybe BUG()? */
+ return -EINVAL;
}
static int dvb_pll_set_params(struct dvb_frontend *fe,
@@ -599,9 +650,35 @@ static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
return 0;
}
+static int dvb_pll_init(struct dvb_frontend *fe)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
+ if (priv->pll_desc->initdata) {
+ struct i2c_msg msg = { .flags = 0,
+ .addr = priv->pll_i2c_address,
+ .buf = priv->pll_desc->initdata + 1,
+ .len = priv->pll_desc->initdata[0] };
+
+ int result;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ return result;
+ }
+ return 0;
+ }
+ /* Shouldn't be called when initdata is NULL, maybe BUG()? */
+ return -EINVAL;
+}
+
static struct dvb_tuner_ops dvb_pll_tuner_ops = {
.release = dvb_pll_release,
.sleep = dvb_pll_sleep,
+ .init = dvb_pll_init,
.set_params = dvb_pll_set_params,
.calc_regs = dvb_pll_calc_regs,
.get_frequency = dvb_pll_get_frequency,
@@ -640,9 +717,14 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops,
sizeof(struct dvb_tuner_ops));
- strncpy(fe->ops.tuner_ops.info.name, desc->name, 128);
+ strncpy(fe->ops.tuner_ops.info.name, desc->name,
+ sizeof(fe->ops.tuner_ops.info.name));
fe->ops.tuner_ops.info.frequency_min = desc->min;
fe->ops.tuner_ops.info.frequency_min = desc->max;
+ if (!desc->initdata)
+ fe->ops.tuner_ops.init = NULL;
+ if (!desc->sleepdata)
+ fe->ops.tuner_ops.sleep = NULL;
fe->tuner_priv = priv;
return fe;
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
index 681186a5e5eb..5209f46f0893 100644
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -12,11 +12,13 @@ struct dvb_pll_desc {
char *name;
u32 min;
u32 max;
+ u32 iffreq;
void (*setbw)(u8 *buf, u32 freq, int bandwidth);
+ u8 *initdata;
+ u8 *sleepdata;
int count;
struct {
u32 limit;
- u32 offset;
u32 stepsize;
u8 config;
u8 cb;
@@ -46,6 +48,7 @@ extern struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261;
extern struct dvb_pll_desc dvb_pll_philips_td1316;
extern struct dvb_pll_desc dvb_pll_thomson_fe6600;
+extern struct dvb_pll_desc dvb_pll_opera1;
extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
u32 freq, int bandwidth);
@@ -59,9 +62,20 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
* @param desc dvb_pll_desc to use.
* @return Frontend pointer on success, NULL on failure
*/
+#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE))
extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
int pll_addr,
struct i2c_adapter *i2c,
struct dvb_pll_desc *desc);
+#else
+static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+ int pll_addr,
+ struct i2c_adapter *i2c,
+ struct dvb_pll_desc *desc)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif
#endif
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index 68aad0f6519f..e25286e2d431 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -475,7 +475,7 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status)
*status |= FE_HAS_CARRIER;
break;
default:
- printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
+ printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
}
return 0;
@@ -534,7 +534,7 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
}
break;
default:
- printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
+ printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__);
}
return 0;
}
diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c
deleted file mode 100644
index 2202d0cc878b..000000000000
--- a/drivers/media/dvb/frontends/lgh06xf.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF
- *
- * 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 "dvb-pll.h"
-#include "lgh06xf.h"
-
-#define LG_H06XF_PLL_I2C_ADDR 0x61
-
-struct lgh06xf_priv {
- struct i2c_adapter *i2c;
- u32 frequency;
-};
-
-static int lgh06xf_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-static int lgh06xf_set_params(struct dvb_frontend* fe,
- struct dvb_frontend_parameters* params)
-{
- struct lgh06xf_priv *priv = fe->tuner_priv;
- u8 buf[4];
- struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0,
- .buf = buf, .len = sizeof(buf) };
- u32 frequency;
- int result;
-
- if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf,
- params->frequency, 0)) < 0)
- return result;
- else
- frequency = result;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
- printk(KERN_WARNING "lgh06xf: %s error "
- "(addr %02x <- %02x, result = %i)\n",
- __FUNCTION__, buf[0], buf[1], result);
- if (result < 0)
- return result;
- else
- return -EREMOTEIO;
- }
-
- /* Set the Auxiliary Byte. */
- buf[0] = buf[2];
- buf[0] &= ~0x20;
- buf[0] |= 0x18;
- buf[1] = 0x50;
- msg.len = 2;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
- printk(KERN_WARNING "lgh06xf: %s error "
- "(addr %02x <- %02x, result = %i)\n",
- __FUNCTION__, buf[0], buf[1], result);
- if (result < 0)
- return result;
- else
- return -EREMOTEIO;
- }
-
- priv->frequency = frequency;
-
- return 0;
-}
-
-static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct lgh06xf_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-static struct dvb_tuner_ops lgh06xf_tuner_ops = {
- .release = lgh06xf_release,
- .set_params = lgh06xf_set_params,
- .get_frequency = lgh06xf_get_frequency,
-};
-
-struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c)
-{
- struct lgh06xf_priv *priv = NULL;
-
- priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->i2c = i2c;
-
- memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name,
- sizeof(fe->ops.tuner_ops.info.name));
-
- fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min;
- fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max;
-
- fe->tuner_priv = priv;
- return fe;
-}
-
-EXPORT_SYMBOL(lgh06xf_attach);
-
-MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support");
-MODULE_AUTHOR("Michael Krufky");
-MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h
deleted file mode 100644
index 510b4bedfb24..000000000000
--- a/drivers/media/dvb/frontends/lgh06xf.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF
- *
- * 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 _LGH06XF_H_
-#define _LGH06XF_H_
-#include "dvb_frontend.h"
-
-#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE))
-extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
- struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
- struct i2c_adapter *i2c)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
- return NULL;
-}
-#endif /* CONFIG_DVB_TUNER_LGH06XF */
-
-#endif /* _LGH06XF_H_ */
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
index 5a3a6e53cda2..4e0aca7c67aa 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -1,6 +1,9 @@
/*
* Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
*
+ *
+ * Copyright (C) 2007 Trent Piepho <xyzzy@speakeasy.org>
+ *
* Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
*
* Based on code from Jack Kelliher (kelliher@xmission.com)
@@ -69,46 +72,70 @@ struct or51132_state
u32 current_frequency;
};
-static int i2c_writebytes (struct or51132_state* state, u8 reg, u8 *buf, int len)
+
+/* Write buffer to demod */
+static int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len)
{
int err;
- struct i2c_msg msg;
- msg.addr = reg;
- msg.flags = 0;
- msg.len = len;
- msg.buf = buf;
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = 0, .buf = (u8*)buf, .len = len };
+ /* msleep(20); */ /* doesn't appear to be necessary */
if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
- printk(KERN_WARNING "or51132: i2c_writebytes error (addr %02x, err == %i)\n", reg, err);
+ printk(KERN_WARNING "or51132: I2C write (addr 0x%02x len %d) error: %d\n",
+ msg.addr, msg.len, err);
return -EREMOTEIO;
}
-
return 0;
}
-static u8 i2c_readbytes (struct or51132_state* state, u8 reg, u8* buf, int len)
+/* Write constant bytes, e.g. or51132_writebytes(state, 0x04, 0x42, 0x00);
+ Less code and more efficient that loading a buffer on the stack with
+ the bytes to send and then calling or51132_writebuf() on that. */
+#define or51132_writebytes(state, data...) \
+ ({ const static u8 _data[] = {data}; \
+ or51132_writebuf(state, _data, sizeof(_data)); })
+
+/* Read data from demod into buffer. Returns 0 on success. */
+static int or51132_readbuf(struct or51132_state *state, u8 *buf, int len)
{
int err;
- struct i2c_msg msg;
- msg.addr = reg;
- msg.flags = I2C_M_RD;
- msg.len = len;
- msg.buf = buf;
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = I2C_M_RD, .buf = buf, .len = len };
+ /* msleep(20); */ /* doesn't appear to be necessary */
if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
- printk(KERN_WARNING "or51132: i2c_readbytes error (addr %02x, err == %i)\n", reg, err);
+ printk(KERN_WARNING "or51132: I2C read (addr 0x%02x len %d) error: %d\n",
+ msg.addr, msg.len, err);
return -EREMOTEIO;
}
-
return 0;
}
+/* Reads a 16-bit demod register. Returns <0 on error. */
+static int or51132_readreg(struct or51132_state *state, u8 reg)
+{
+ u8 buf[2] = { 0x04, reg };
+ struct i2c_msg msg[2] = {
+ {.addr = state->config->demod_address, .flags = 0,
+ .buf = buf, .len = 2 },
+ {.addr = state->config->demod_address, .flags = I2C_M_RD,
+ .buf = buf, .len = 2 }};
+ int err;
+
+ if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) {
+ printk(KERN_WARNING "or51132: I2C error reading register %d: %d\n",
+ reg, err);
+ return -EREMOTEIO;
+ }
+ return le16_to_cpup((u16*)buf);
+}
+
static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
{
struct or51132_state* state = fe->demodulator_priv;
- static u8 run_buf[] = {0x7F,0x01};
+ const static u8 run_buf[] = {0x7F,0x01};
u8 rec_buf[8];
- u8 cmd_buf[3];
u32 firmwareAsize, firmwareBsize;
int i,ret;
@@ -121,30 +148,21 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware
dprintk("FirmwareB is %i bytes\n",firmwareBsize);
/* Upload firmware */
- if ((ret = i2c_writebytes(state,state->config->demod_address,
- &fw->data[8],firmwareAsize))) {
+ if ((ret = or51132_writebuf(state, &fw->data[8], firmwareAsize))) {
printk(KERN_WARNING "or51132: load_firmware error 1\n");
return ret;
}
- msleep(1); /* 1ms */
- if ((ret = i2c_writebytes(state,state->config->demod_address,
- &fw->data[8+firmwareAsize],firmwareBsize))) {
+ if ((ret = or51132_writebuf(state, &fw->data[8+firmwareAsize],
+ firmwareBsize))) {
printk(KERN_WARNING "or51132: load_firmware error 2\n");
return ret;
}
- msleep(1); /* 1ms */
- if ((ret = i2c_writebytes(state,state->config->demod_address,
- run_buf,2))) {
+ if ((ret = or51132_writebuf(state, run_buf, 2))) {
printk(KERN_WARNING "or51132: load_firmware error 3\n");
return ret;
}
-
- /* Wait at least 5 msec */
- msleep(20); /* 10ms */
-
- if ((ret = i2c_writebytes(state,state->config->demod_address,
- run_buf,2))) {
+ if ((ret = or51132_writebuf(state, run_buf, 2))) {
printk(KERN_WARNING "or51132: load_firmware error 4\n");
return ret;
}
@@ -154,43 +172,25 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware
/* Read back ucode version to besure we loaded correctly and are really up and running */
/* Get uCode version */
- cmd_buf[0] = 0x10;
- cmd_buf[1] = 0x10;
- cmd_buf[2] = 0x00;
- msleep(20); /* 20ms */
- if ((ret = i2c_writebytes(state,state->config->demod_address,
- cmd_buf,3))) {
+ if ((ret = or51132_writebytes(state, 0x10, 0x10, 0x00))) {
printk(KERN_WARNING "or51132: load_firmware error a\n");
return ret;
}
-
- cmd_buf[0] = 0x04;
- cmd_buf[1] = 0x17;
- msleep(20); /* 20ms */
- if ((ret = i2c_writebytes(state,state->config->demod_address,
- cmd_buf,2))) {
+ if ((ret = or51132_writebytes(state, 0x04, 0x17))) {
printk(KERN_WARNING "or51132: load_firmware error b\n");
return ret;
}
-
- cmd_buf[0] = 0x00;
- cmd_buf[1] = 0x00;
- msleep(20); /* 20ms */
- if ((ret = i2c_writebytes(state,state->config->demod_address,
- cmd_buf,2))) {
+ if ((ret = or51132_writebytes(state, 0x00, 0x00))) {
printk(KERN_WARNING "or51132: load_firmware error c\n");
return ret;
}
-
- for(i=0;i<4;i++) {
- msleep(20); /* 20ms */
+ for (i=0;i<4;i++) {
/* Once upon a time, this command might have had something
to do with getting the firmware version, but it's
not used anymore:
{0x04,0x00,0x30,0x00,i+1} */
/* Read 8 bytes, two bytes at a time */
- if ((ret = i2c_readbytes(state,state->config->demod_address,
- &rec_buf[i*2],2))) {
+ if ((ret = or51132_readbuf(state, &rec_buf[i*2], 2))) {
printk(KERN_WARNING
"or51132: load_firmware error d - %d\n",i);
return ret;
@@ -204,12 +204,7 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware
rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f,
rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f);
- cmd_buf[0] = 0x10;
- cmd_buf[1] = 0x00;
- cmd_buf[2] = 0x00;
- msleep(20); /* 20ms */
- if ((ret = i2c_writebytes(state,state->config->demod_address,
- cmd_buf,3))) {
+ if ((ret = or51132_writebytes(state, 0x10, 0x00, 0x00))) {
printk(KERN_WARNING "or51132: load_firmware error e\n");
return ret;
}
@@ -241,70 +236,55 @@ static int or51132_sleep(struct dvb_frontend* fe)
static int or51132_setmode(struct dvb_frontend* fe)
{
struct or51132_state* state = fe->demodulator_priv;
- unsigned char cmd_buf[3];
+ u8 cmd_buf1[3] = {0x04, 0x01, 0x5f};
+ u8 cmd_buf2[3] = {0x1c, 0x00, 0 };
dprintk("setmode %d\n",(int)state->current_modulation);
- /* set operation mode in Receiver 1 register; */
- cmd_buf[0] = 0x04;
- cmd_buf[1] = 0x01;
+
switch (state->current_modulation) {
- case QAM_256:
- case QAM_64:
- case QAM_AUTO:
- /* Auto-deinterleave; MPEG ser, MPEG2tr, phase noise-high*/
- cmd_buf[2] = 0x5F;
- break;
case VSB_8:
- /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high*/
- cmd_buf[2] = 0x50;
+ /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high */
+ cmd_buf1[2] = 0x50;
+ /* REC MODE inv IF spectrum, Normal */
+ cmd_buf2[1] = 0x03;
+ /* Channel MODE ATSC/VSB8 */
+ cmd_buf2[2] = 0x06;
break;
- default:
- printk("setmode:Modulation set to unsupported value\n");
- };
- if (i2c_writebytes(state,state->config->demod_address,
- cmd_buf,3)) {
- printk(KERN_WARNING "or51132: set_mode error 1\n");
- return -1;
- }
- dprintk("or51132: set #1 to %02x\n", cmd_buf[2]);
-
- /* Set operation mode in Receiver 6 register */
- cmd_buf[0] = 0x1C;
- switch (state->current_modulation) {
+ /* All QAM modes are:
+ Auto-deinterleave; MPEGser, MPEG2tr, phase noise-high
+ REC MODE Normal Carrier Lock */
case QAM_AUTO:
- /* REC MODE Normal Carrier Lock */
- cmd_buf[1] = 0x00;
/* Channel MODE Auto QAM64/256 */
- cmd_buf[2] = 0x4f;
+ cmd_buf2[2] = 0x4f;
break;
case QAM_256:
- /* REC MODE Normal Carrier Lock */
- cmd_buf[1] = 0x00;
/* Channel MODE QAM256 */
- cmd_buf[2] = 0x45;
+ cmd_buf2[2] = 0x45;
break;
case QAM_64:
- /* REC MODE Normal Carrier Lock */
- cmd_buf[1] = 0x00;
/* Channel MODE QAM64 */
- cmd_buf[2] = 0x43;
- break;
- case VSB_8:
- /* REC MODE inv IF spectrum, Normal */
- cmd_buf[1] = 0x03;
- /* Channel MODE ATSC/VSB8 */
- cmd_buf[2] = 0x06;
+ cmd_buf2[2] = 0x43;
break;
default:
- printk("setmode: Modulation set to unsupported value\n");
- };
- msleep(20); /* 20ms */
- if (i2c_writebytes(state,state->config->demod_address,
- cmd_buf,3)) {
+ printk(KERN_WARNING
+ "or51132: setmode: Modulation set to unsupported value (%d)\n",
+ state->current_modulation);
+ return -EINVAL;
+ }
+
+ /* Set Receiver 1 register */
+ if (or51132_writebuf(state, cmd_buf1, 3)) {
+ printk(KERN_WARNING "or51132: set_mode error 1\n");
+ return -EREMOTEIO;
+ }
+ dprintk("set #1 to %02x\n", cmd_buf1[2]);
+
+ /* Set operation mode in Receiver 6 register */
+ if (or51132_writebuf(state, cmd_buf2, 3)) {
printk(KERN_WARNING "or51132: set_mode error 2\n");
- return -1;
+ return -EREMOTEIO;
}
- dprintk("or51132: set #6 to 0x%02x%02x\n", cmd_buf[1], cmd_buf[2]);
+ dprintk("set #6 to 0x%02x%02x\n", cmd_buf2[1], cmd_buf2[2]);
return 0;
}
@@ -401,28 +381,23 @@ static int or51132_get_parameters(struct dvb_frontend* fe,
struct dvb_frontend_parameters *param)
{
struct or51132_state* state = fe->demodulator_priv;
- u8 buf[2];
+ int status;
+ int retry = 1;
+start:
/* Receiver Status */
- buf[0]=0x04;
- buf[1]=0x00;
- msleep(30); /* 30ms */
- if (i2c_writebytes(state,state->config->demod_address,buf,2)) {
- printk(KERN_WARNING "or51132: get_parameters write error\n");
- return -EREMOTEIO;
- }
- msleep(30); /* 30ms */
- if (i2c_readbytes(state,state->config->demod_address,buf,2)) {
- printk(KERN_WARNING "or51132: get_parameters read error\n");
+ if ((status = or51132_readreg(state, 0x00)) < 0) {
+ printk(KERN_WARNING "or51132: get_parameters: error reading receiver status\n");
return -EREMOTEIO;
}
- switch(buf[0]) {
+ switch(status&0xff) {
case 0x06: param->u.vsb.modulation = VSB_8; break;
case 0x43: param->u.vsb.modulation = QAM_64; break;
case 0x45: param->u.vsb.modulation = QAM_256; break;
default:
+ if (retry--) goto start;
printk(KERN_WARNING "or51132: unknown status 0x%02x\n",
- buf[0]);
+ status&0xff);
return -EREMOTEIO;
}
@@ -438,32 +413,21 @@ static int or51132_get_parameters(struct dvb_frontend* fe,
static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
{
struct or51132_state* state = fe->demodulator_priv;
- unsigned char rec_buf[2];
- unsigned char snd_buf[2];
- *status = 0;
+ int reg;
/* Receiver Status */
- snd_buf[0]=0x04;
- snd_buf[1]=0x00;
- msleep(30); /* 30ms */
- if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: read_status write error\n");
- return -1;
- }
- msleep(30); /* 30ms */
- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: read_status read error\n");
- return -1;
- }
- dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
-
- if (rec_buf[1] & 0x01) { /* Receiver Lock */
- *status |= FE_HAS_SIGNAL;
- *status |= FE_HAS_CARRIER;
- *status |= FE_HAS_VITERBI;
- *status |= FE_HAS_SYNC;
- *status |= FE_HAS_LOCK;
+ if ((reg = or51132_readreg(state, 0x00)) < 0) {
+ printk(KERN_WARNING "or51132: read_status: error reading receiver status: %d\n", reg);
+ *status = 0;
+ return -EREMOTEIO;
}
+ dprintk("%s: read_status %04x\n", __FUNCTION__, reg);
+
+ if (reg & 0x0100) /* Receiver Lock */
+ *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|
+ FE_HAS_SYNC|FE_HAS_LOCK;
+ else
+ *status = 0;
return 0;
}
@@ -506,47 +470,30 @@ static u32 calculate_snr(u32 mse, u32 c)
static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
{
struct or51132_state* state = fe->demodulator_priv;
- u8 rec_buf[2];
- u8 snd_buf[2];
- u32 noise;
- u32 c;
- u32 usK;
-
- /* Register is same for VSB or QAM firmware */
- snd_buf[0]=0x04;
- snd_buf[1]=0x02; /* SNR after Equalizer */
- msleep(30); /* 30ms */
- if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: snr write error\n");
- return -EREMOTEIO;
- }
- msleep(30); /* 30ms */
- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: snr read error\n");
+ int noise, reg;
+ u32 c, usK = 0;
+ int retry = 1;
+
+start:
+ /* SNR after Equalizer */
+ noise = or51132_readreg(state, 0x02);
+ if (noise < 0) {
+ printk(KERN_WARNING "or51132: read_snr: error reading equalizer\n");
return -EREMOTEIO;
}
- noise = rec_buf[0] | (rec_buf[1] << 8);
- dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise);
+ dprintk("read_snr noise (%d)\n", noise);
/* Read status, contains modulation type for QAM_AUTO and
NTSC filter for VSB */
- snd_buf[0]=0x04;
- snd_buf[1]=0x00; /* Status register */
- msleep(30); /* 30ms */
- if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: status write error\n");
- return -EREMOTEIO;
- }
- msleep(30); /* 30ms */
- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: status read error\n");
+ reg = or51132_readreg(state, 0x00);
+ if (reg < 0) {
+ printk(KERN_WARNING "or51132: read_snr: error reading receiver status\n");
return -EREMOTEIO;
}
- usK = 0;
- switch (rec_buf[0]) {
+ switch (reg&0xff) {
case 0x06:
- usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0;
+ if (reg & 0x1000) usK = 3 << 24;
/* Fall through to QAM64 case */
case 0x43:
c = 150204167;
@@ -555,11 +502,12 @@ static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
c = 150290396;
break;
default:
- printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]);
+ printk(KERN_WARNING "or51132: unknown status 0x%02x\n", reg&0xff);
+ if (retry--) goto start;
return -EREMOTEIO;
}
dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__,
- rec_buf[0], rec_buf[1]&0x10?"n":"ff");
+ reg&0xff, reg&0x1000?"n":"ff");
/* Calculate SNR using noise, c, and NTSC rejection correction */
state->snr = calculate_snr(noise, c) - usK;
@@ -671,6 +619,7 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver");
MODULE_AUTHOR("Kirk Lapray");
+MODULE_AUTHOR("Trent Piepho");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(or51132_attach);
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c
index 5b9c5bb29b23..110536843e8e 100644
--- a/drivers/media/dvb/frontends/tda10021.c
+++ b/drivers/media/dvb/frontends/tda10021.c
@@ -30,13 +30,13 @@
#include <linux/slab.h>
#include "dvb_frontend.h"
-#include "tda10021.h"
+#include "tda1002x.h"
struct tda10021_state {
struct i2c_adapter* i2c;
/* configuration settings */
- const struct tda10021_config* config;
+ const struct tda1002x_config* config;
struct dvb_frontend frontend;
u8 pwm;
@@ -53,9 +53,6 @@ struct tda10021_state {
static int verbose;
#define XIN 57840000UL
-#define DISABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0)
-#define ENABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0)
-#define HAS_INVERSION(reg0) (!(reg0 & 0x20))
#define FIN (XIN >> 4)
@@ -64,7 +61,7 @@ static u8 tda10021_inittab[0x40]=
{
0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
- 0xb8, 0x3f, 0xa0, 0x00, 0xcd, 0x01, 0x00, 0xff,
+ 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff,
0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
@@ -97,7 +94,8 @@ static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
int ret;
ret = i2c_transfer (state->i2c, msg, 2);
- if (ret != 2)
+ // Don't print an error message if the id is read.
+ if (ret != 2 && reg != 0x1a)
printk("DVB: TDA10021: %s: readreg error (ret == %i)\n",
__FUNCTION__, ret);
return b1[0];
@@ -136,10 +134,10 @@ static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0,
{
reg0 |= state->reg0 & 0x63;
- if (INVERSION_ON == inversion)
- ENABLE_INVERSION(reg0);
- else if (INVERSION_OFF == inversion)
- DISABLE_INVERSION(reg0);
+ if ((INVERSION_ON == inversion) ^ (state->config->invert == 0))
+ reg0 &= ~0x20;
+ else
+ reg0 |= 0x20;
_tda10021_writereg (state, 0x00, reg0 & 0xfe);
_tda10021_writereg (state, 0x00, reg0 | 0x01);
@@ -201,16 +199,6 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate
return 0;
}
-static 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;
@@ -258,6 +246,9 @@ static int tda10021_set_parameters (struct dvb_frontend *fe,
if (qam < 0 || qam > 5)
return -EINVAL;
+ if (p->inversion != INVERSION_ON && p->inversion != INVERSION_OFF)
+ return -EINVAL;
+
//printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate);
if (fe->ops.tuner_ops.set_params) {
@@ -366,7 +357,7 @@ static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa
-((s32)p->u.qam.symbol_rate * afc) >> 10);
}
- p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
+ p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF;
p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
p->u.qam.fec_inner = FEC_NONE;
@@ -408,11 +399,12 @@ static void tda10021_release(struct dvb_frontend* fe)
static struct dvb_frontend_ops tda10021_ops;
-struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
struct i2c_adapter* i2c,
u8 pwm)
{
struct tda10021_state* state = NULL;
+ u8 id;
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
@@ -425,7 +417,11 @@ struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
state->reg0 = tda10021_inittab[0];
/* check if the demod is there */
- if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
+ id = tda10021_readreg(state, 0x1a);
+ if ((id & 0xf0) != 0x70) goto error;
+
+ printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n",
+ state->config->demod_address, id);
/* create dvb_frontend */
memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
@@ -447,7 +443,7 @@ static struct dvb_frontend_ops tda10021_ops = {
.frequency_max = 858000000,
.symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */
.symbol_rate_max = (XIN/2)/4, /* SACLK/4 */
-#if 0
+ #if 0
.frequency_tolerance = ???,
.symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */
#endif
@@ -461,7 +457,6 @@ 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/tda10023.c b/drivers/media/dvb/frontends/tda10023.c
new file mode 100644
index 000000000000..da796e784be3
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10023.c
@@ -0,0 +1,540 @@
+/*
+ TDA10023 - DVB-C decoder
+ (as used in Philips CU1216-3 NIM and the Reelbox DVB-C tuner card)
+
+ Copyright (C) 2005 Georg Acher, BayCom GmbH (acher at baycom dot de)
+ Copyright (c) 2006 Hartmut Birr (e9hack at gmail dot com)
+
+ Remotely based on tda10021.c
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+ Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+ Support for TDA10021
+
+ 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/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "tda1002x.h"
+
+
+struct tda10023_state {
+ struct i2c_adapter* i2c;
+ /* configuration settings */
+ const struct tda1002x_config* config;
+ struct dvb_frontend frontend;
+
+ u8 pwm;
+ u8 reg0;
+};
+
+
+#define dprintk(x...)
+
+static int verbose;
+
+#define XTAL 28920000UL
+#define PLL_M 8UL
+#define PLL_P 4UL
+#define PLL_N 1UL
+#define SYSCLK (XTAL*PLL_M/(PLL_N*PLL_P)) // -> 57840000
+
+static u8 tda10023_inittab[]={
+ // reg mask val
+ 0x2a,0xff,0x02, // PLL3, Bypass, Power Down
+ 0xff,0x64,0x00, // Sleep 100ms
+ 0x2a,0xff,0x03, // PLL3, Bypass, Power Down
+ 0xff,0x64,0x00, // Sleep 100ms
+ 0x28,0xff,PLL_M-1, // PLL1 M=8
+ 0x29,0xff,((PLL_P-1)<<6)|(PLL_N-1), // PLL2
+ 0x00,0xff,0x23, // GPR FSAMPLING=1
+ 0x2a,0xff,0x08, // PLL3 PSACLK=1
+ 0xff,0x64,0x00, // Sleep 100ms
+ 0x1f,0xff,0x00, // RESET
+ 0xff,0x64,0x00, // Sleep 100ms
+ 0xe6,0x0c,0x04, // RSCFG_IND
+ 0x10,0xc0,0x80, // DECDVBCFG1 PBER=1
+
+ 0x0e,0xff,0x82, // GAIN1
+ 0x03,0x08,0x08, // CLKCONF DYN=1
+ 0x2e,0xbf,0x30, // AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 PPWMTUN=0 PPWMIF=0
+ 0x01,0xff,0x30, // AGCREF
+ 0x1e,0x84,0x84, // CONTROL SACLK_ON=1
+ 0x1b,0xff,0xc8, // ADC TWOS=1
+ 0x3b,0xff,0xff, // IFMAX
+ 0x3c,0xff,0x00, // IFMIN
+ 0x34,0xff,0x00, // PWMREF
+ 0x35,0xff,0xff, // TUNMAX
+ 0x36,0xff,0x00, // TUNMIN
+ 0x06,0xff,0x7f, // EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 // 0x77
+ 0x1c,0x30,0x30, // EQCONF2 STEPALGO=SGNALGO=1
+ 0x37,0xff,0xf6, // DELTAF_LSB
+ 0x38,0xff,0xff, // DELTAF_MSB
+ 0x02,0xff,0x93, // AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3
+ 0x2d,0xff,0xf6, // SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2
+ 0x04,0x10,0x00, // SWRAMP=1
+ 0x12,0xff,0xa1, // INTP1 POCLKP=1 FEL=1 MFS=0
+ 0x2b,0x01,0xa1, // INTS1
+ 0x20,0xff,0x04, // INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=?
+ 0x2c,0xff,0x0d, // INTP/S TRIP=0 TRIS=0
+ 0xc4,0xff,0x00,
+ 0xc3,0x30,0x00,
+ 0xb5,0xff,0x19, // ERAGC_THD
+ 0x00,0x03,0x01, // GPR, CLBS soft reset
+ 0x00,0x03,0x03, // GPR, CLBS soft reset
+ 0xff,0x64,0x00, // Sleep 100ms
+ 0xff,0xff,0xff
+};
+
+static u8 tda10023_readreg (struct tda10023_state* state, u8 reg)
+{
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+ int ret;
+
+ ret = i2c_transfer (state->i2c, msg, 2);
+ if (ret != 2)
+ printk("DVB: TDA10023: %s: readreg error (ret == %i)\n",
+ __FUNCTION__, ret);
+ return b1[0];
+}
+
+static int tda10023_writereg (struct tda10023_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 };
+ int ret;
+
+ ret = i2c_transfer (state->i2c, &msg, 1);
+ if (ret != 1)
+ printk("DVB: TDA10023(%d): %s, writereg error "
+ "(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+ state->frontend.dvb->num, __FUNCTION__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+
+static int tda10023_writebit (struct tda10023_state* state, u8 reg, u8 mask,u8 data)
+{
+ if (mask==0xff)
+ return tda10023_writereg(state, reg, data);
+ else {
+ u8 val;
+ val=tda10023_readreg(state,reg);
+ val&=~mask;
+ val|=(data&mask);
+ return tda10023_writereg(state, reg, val);
+ }
+}
+
+static void tda10023_writetab(struct tda10023_state* state, u8* tab)
+{
+ u8 r,m,v;
+ while (1) {
+ r=*tab++;
+ m=*tab++;
+ v=*tab++;
+ if (r==0xff) {
+ if (m==0xff)
+ break;
+ else
+ msleep(m);
+ }
+ else
+ tda10023_writebit(state,r,m,v);
+ }
+}
+
+//get access to tuner
+static int lock_tuner(struct tda10023_state* state)
+{
+ u8 buf[2] = { 0x0f, 0xc0 };
+ struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+ if(i2c_transfer(state->i2c, &msg, 1) != 1)
+ {
+ printk("tda10023: lock tuner fails\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+//release access from tuner
+static int unlock_tuner(struct tda10023_state* state)
+{
+ u8 buf[2] = { 0x0f, 0x40 };
+ struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+ if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
+ {
+ printk("tda10023: unlock tuner fails\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int tda10023_setup_reg0 (struct tda10023_state* state, u8 reg0)
+{
+ reg0 |= state->reg0 & 0x63;
+
+ tda10023_writereg (state, 0x00, reg0 & 0xfe);
+ tda10023_writereg (state, 0x00, reg0 | 0x01);
+
+ state->reg0 = reg0;
+ return 0;
+}
+
+static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
+{
+ s32 BDR;
+ s32 BDRI;
+ s16 SFIL=0;
+ u16 NDEC = 0;
+
+ if (sr > (SYSCLK/(2*4)))
+ sr=SYSCLK/(2*4);
+
+ if (sr<870000)
+ sr=870000;
+
+ if (sr < (u32)(SYSCLK/98.40)) {
+ NDEC=3;
+ SFIL=1;
+ } else if (sr<(u32)(SYSCLK/64.0)) {
+ NDEC=3;
+ SFIL=0;
+ } else if (sr<(u32)(SYSCLK/49.2)) {
+ NDEC=2;
+ SFIL=1;
+ } else if (sr<(u32)(SYSCLK/32.0)) {
+ NDEC=2;
+ SFIL=0;
+ } else if (sr<(u32)(SYSCLK/24.6)) {
+ NDEC=1;
+ SFIL=1;
+ } else if (sr<(u32)(SYSCLK/16.0)) {
+ NDEC=1;
+ SFIL=0;
+ } else if (sr<(u32)(SYSCLK/12.3)) {
+ NDEC=0;
+ SFIL=1;
+ }
+
+ BDRI=SYSCLK*16;
+ BDRI>>=NDEC;
+ BDRI +=sr/2;
+ BDRI /=sr;
+
+ if (BDRI>255)
+ BDRI=255;
+
+ {
+ u64 BDRX;
+
+ BDRX=1<<(24+NDEC);
+ BDRX*=sr;
+ do_div(BDRX,SYSCLK); // BDRX/=SYSCLK;
+
+ BDR=(s32)BDRX;
+ }
+// printk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",sr,BDR,BDRI,NDEC);
+ tda10023_writebit (state, 0x03, 0xc0, NDEC<<6);
+ tda10023_writereg (state, 0x0a, BDR&255);
+ tda10023_writereg (state, 0x0b, (BDR>>8)&255);
+ tda10023_writereg (state, 0x0c, (BDR>>16)&31);
+ tda10023_writereg (state, 0x0d, BDRI);
+ tda10023_writereg (state, 0x3d, (SFIL<<7));
+ return 0;
+}
+
+static int tda10023_init (struct dvb_frontend *fe)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+
+ dprintk("DVB: TDA10023(%d): init chip\n", fe->adapter->num);
+
+ tda10023_writetab(state, tda10023_inittab);
+
+ return 0;
+}
+
+static int tda10023_set_parameters (struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+
+ static int qamvals[6][6] = {
+ // QAM LOCKTHR MSETH AREF AGCREFNYQ ERAGCNYQ_THD
+ { (5<<2), 0x78, 0x8c, 0x96, 0x78, 0x4c }, // 4 QAM
+ { (0<<2), 0x87, 0xa2, 0x91, 0x8c, 0x57 }, // 16 QAM
+ { (1<<2), 0x64, 0x74, 0x96, 0x8c, 0x57 }, // 32 QAM
+ { (2<<2), 0x46, 0x43, 0x6a, 0x6a, 0x44 }, // 64 QAM
+ { (3<<2), 0x36, 0x34, 0x7e, 0x78, 0x4c }, // 128 QAM
+ { (4<<2), 0x26, 0x23, 0x6c, 0x5c, 0x3c }, // 256 QAM
+ };
+
+ int qam = p->u.qam.modulation;
+
+ if (qam < 0 || qam > 5)
+ return -EINVAL;
+
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, p);
+ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ tda10023_set_symbolrate (state, p->u.qam.symbol_rate);
+ tda10023_writereg (state, 0x05, qamvals[qam][1]);
+ tda10023_writereg (state, 0x08, qamvals[qam][2]);
+ tda10023_writereg (state, 0x09, qamvals[qam][3]);
+ tda10023_writereg (state, 0xb4, qamvals[qam][4]);
+ tda10023_writereg (state, 0xb6, qamvals[qam][5]);
+
+// tda10023_writereg (state, 0x04, (p->inversion?0x12:0x32));
+// tda10023_writebit (state, 0x04, 0x60, (p->inversion?0:0x20));
+ tda10023_writebit (state, 0x04, 0x40, 0x40);
+ tda10023_setup_reg0 (state, qamvals[qam][0]);
+
+ return 0;
+}
+
+static int tda10023_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+ int sync;
+
+ *status = 0;
+
+ //0x11[1] == CARLOCK -> Carrier locked
+ //0x11[2] == FSYNC -> Frame synchronisation
+ //0x11[3] == FEL -> Front End locked
+ //0x11[6] == NODVB -> DVB Mode Information
+ sync = tda10023_readreg (state, 0x11);
+
+ if (sync & 2)
+ *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
+
+ if (sync & 4)
+ *status |= FE_HAS_SYNC|FE_HAS_VITERBI;
+
+ if (sync & 8)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int tda10023_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+ u8 a,b,c;
+ a=tda10023_readreg(state, 0x14);
+ b=tda10023_readreg(state, 0x15);
+ c=tda10023_readreg(state, 0x16)&0xf;
+ tda10023_writebit (state, 0x10, 0xc0, 0x00);
+
+ *ber = a | (b<<8)| (c<<16);
+ return 0;
+}
+
+static int tda10023_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+ u8 ifgain=tda10023_readreg(state, 0x2f);
+
+ u16 gain = ((255-tda10023_readreg(state, 0x17))) + (255-ifgain)/16;
+ // Max raw value is about 0xb0 -> Normalize to >0xf0 after 0x90
+ if (gain>0x90)
+ gain=gain+2*(gain-0x90);
+ if (gain>255)
+ gain=255;
+
+ *strength = (gain<<8)|gain;
+ return 0;
+}
+
+static int tda10023_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+
+ u8 quality = ~tda10023_readreg(state, 0x18);
+ *snr = (quality << 8) | quality;
+ return 0;
+}
+
+static int tda10023_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+ u8 a,b,c,d;
+ a= tda10023_readreg (state, 0x74);
+ b= tda10023_readreg (state, 0x75);
+ c= tda10023_readreg (state, 0x76);
+ d= tda10023_readreg (state, 0x77);
+ *ucblocks = a | (b<<8)|(c<<16)|(d<<24);
+
+ tda10023_writebit (state, 0x10, 0x20,0x00);
+ tda10023_writebit (state, 0x10, 0x20,0x20);
+ tda10023_writebit (state, 0x13, 0x01, 0x00);
+
+ return 0;
+}
+
+static int tda10023_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+ int sync,inv;
+ s8 afc = 0;
+
+ sync = tda10023_readreg(state, 0x11);
+ afc = tda10023_readreg(state, 0x19);
+ inv = tda10023_readreg(state, 0x04);
+
+ if (verbose) {
+ /* AFC only valid when carrier has been recovered */
+ printk(sync & 2 ? "DVB: TDA10023(%d): AFC (%d) %dHz\n" :
+ "DVB: TDA10023(%d): [AFC (%d) %dHz]\n",
+ state->frontend.dvb->num, afc,
+ -((s32)p->u.qam.symbol_rate * afc) >> 10);
+ }
+
+ p->inversion = (inv&0x20?0:1);
+ p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
+
+ p->u.qam.fec_inner = FEC_NONE;
+ p->frequency = ((p->frequency + 31250) / 62500) * 62500;
+
+ if (sync & 2)
+ p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10;
+
+ return 0;
+}
+
+static int tda10023_sleep(struct dvb_frontend* fe)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+
+ tda10023_writereg (state, 0x1b, 0x02); /* pdown ADC */
+ tda10023_writereg (state, 0x00, 0x80); /* standby */
+
+ return 0;
+}
+
+static int tda10023_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+
+ if (enable) {
+ lock_tuner(state);
+ } else {
+ unlock_tuner(state);
+ }
+ return 0;
+}
+
+static void tda10023_release(struct dvb_frontend* fe)
+{
+ struct tda10023_state* state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops tda10023_ops;
+
+struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
+ struct i2c_adapter* i2c,
+ u8 pwm)
+{
+ struct tda10023_state* state = NULL;
+ int i;
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
+ state->pwm = pwm;
+ for (i=0; i < sizeof(tda10023_inittab)/sizeof(*tda10023_inittab);i+=3) {
+ if (tda10023_inittab[i] == 0x00) {
+ state->reg0 = tda10023_inittab[i+2];
+ break;
+ }
+ }
+
+ // Wakeup if in standby
+ tda10023_writereg (state, 0x00, 0x33);
+ /* check if the demod is there */
+ if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops tda10023_ops = {
+
+ .info = {
+ .name = "Philips TDA10023 DVB-C",
+ .type = FE_QAM,
+ .frequency_stepsize = 62500,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .symbol_rate_min = (SYSCLK/2)/64, /* SACLK/64 == (SYSCLK/2)/64 */
+ .symbol_rate_max = (SYSCLK/2)/4, /* SACLK/4 */
+ .caps = 0x400 | //FE_CAN_QAM_4
+ FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = tda10023_release,
+
+ .init = tda10023_init,
+ .sleep = tda10023_sleep,
+ .i2c_gate_ctrl = tda10023_i2c_gate_ctrl,
+
+ .set_frontend = tda10023_set_parameters,
+ .get_frontend = tda10023_get_frontend,
+
+ .read_status = tda10023_read_status,
+ .read_ber = tda10023_read_ber,
+ .read_signal_strength = tda10023_read_signal_strength,
+ .read_snr = tda10023_read_snr,
+ .read_ucblocks = tda10023_read_ucblocks,
+};
+
+
+MODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver");
+MODULE_AUTHOR("Georg Acher, Hartmut Birr");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10023_attach);
diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda1002x.h
index e3da780108f6..e9094d8123f6 100644
--- a/drivers/media/dvb/frontends/tda10021.h
+++ b/drivers/media/dvb/frontends/tda1002x.h
@@ -1,6 +1,6 @@
/*
- TDA10021 - Single Chip Cable Channel Receiver driver module
- used on the the Siemens DVB-C cards
+ TDA10021/TDA10023 - Single Chip Cable Channel Receiver driver module
+ used on the the Siemens DVB-C cards
Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
@@ -21,22 +21,23 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifndef TDA10021_H
-#define TDA10021_H
+#ifndef TDA1002x_H
+#define TDA1002x_H
#include <linux/dvb/frontend.h>
-struct tda10021_config
+struct tda1002x_config
{
/* the demodulator's i2c address */
u8 demod_address;
+ u8 invert;
};
#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE))
-extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
struct i2c_adapter* i2c, u8 pwm);
#else
-static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
struct i2c_adapter* i2c, u8 pwm)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
@@ -44,12 +45,16 @@ static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config*
}
#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;
+#if defined(CONFIG_DVB_TDA10023) || (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE))
+extern struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
+ struct i2c_adapter* i2c, u8 pwm);
+#else
+static inline struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
+ struct i2c_adapter* i2c, u8 pwm)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
}
+#endif // CONFIG_DVB_TDA10023
-#endif // TDA10021_H
+#endif // TDA1002x_H
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index f4a9cf9d26d0..33a84372c9e6 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -40,20 +40,6 @@
#include "dvb_frontend.h"
#include "tda1004x.h"
-enum tda1004x_demod {
- TDA1004X_DEMOD_TDA10045,
- TDA1004X_DEMOD_TDA10046,
-};
-
-struct tda1004x_state {
- struct i2c_adapter* i2c;
- const struct tda1004x_config* config;
- struct dvb_frontend frontend;
-
- /* private demod data */
- enum tda1004x_demod demod_type;
-};
-
static int debug;
#define dprintk(args...) \
do { \
@@ -507,35 +493,51 @@ static int tda10046_fwupload(struct dvb_frontend* fe)
tda1004x_write_byteI(state, TDA1004X_CONFC4, 0x80);
}
tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
+ /* set GPIO 1 and 3 */
+ if (state->config->gpio_config != TDA10046_GPTRI) {
+ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33);
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f);
+ }
/* let the clocks recover from sleep */
- msleep(5);
+ msleep(10);
/* The PLLs need to be reprogrammed after sleep */
tda10046_init_plls(fe);
+ tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0);
/* don't re-upload unless necessary */
if (tda1004x_check_upload_ok(state) == 0)
return 0;
+ printk(KERN_INFO "tda1004x: trying to boot from eeprom\n");
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4);
+ msleep(300);
+ /* don't re-upload unless necessary */
+ if (tda1004x_check_upload_ok(state) == 0)
+ return 0;
+
if (state->config->request_firmware != NULL) {
/* request the firmware, this will block until someone uploads it */
printk(KERN_INFO "tda1004x: waiting for firmware upload...\n");
ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
if (ret) {
- printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
- return ret;
+ /* remain compatible to old bug: try to load with tda10045 image name */
+ ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
+ if (ret) {
+ printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
+ return ret;
+ } else {
+ printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n",
+ TDA10046_DEFAULT_FIRMWARE);
+ }
}
- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
- ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
- release_firmware(fw);
- if (ret)
- return ret;
} else {
- /* boot from firmware eeprom */
- printk(KERN_INFO "tda1004x: booting from eeprom\n");
- tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4);
- msleep(300);
+ printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n");
+ return -EIO;
}
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
+ ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
+ release_firmware(fw);
return tda1004x_check_upload_ok(state);
}
@@ -638,37 +640,33 @@ static int tda10046_init(struct dvb_frontend* fe)
switch (state->config->agc_config) {
case TDA10046_AGC_DEFAULT:
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup
- tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
break;
case TDA10046_AGC_IFO_AUTO_NEG:
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
- tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
break;
case TDA10046_AGC_IFO_AUTO_POS:
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
- tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities
- break;
- case TDA10046_AGC_TDA827X_GP11:
- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
- tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
- tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
- tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities
- break;
- case TDA10046_AGC_TDA827X_GP00:
- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
- tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
- tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
- tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00); // set AGC polarities
break;
- case TDA10046_AGC_TDA827X_GP01:
+ case TDA10046_AGC_TDA827X:
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
- tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
break;
}
+ if (state->config->ts_mode == 0) {
+ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40);
+ tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
+ } else {
+ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80);
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10,
+ state->config->invert_oclk << 4);
+ }
tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38);
- tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on
+ tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on
tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // }
tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // }
@@ -678,7 +676,6 @@ static int tda10046_init(struct dvb_frontend* fe)
tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config
// tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes
- tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
return 0;
}
@@ -705,7 +702,8 @@ static int tda1004x_set_fe(struct dvb_frontend* fe,
// set frequency
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.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
}
// Hardcoded to use auto as much as possible on the TDA10045 as it
@@ -1165,6 +1163,7 @@ static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber)
static int tda1004x_sleep(struct dvb_frontend* fe)
{
struct tda1004x_state* state = fe->demodulator_priv;
+ int gpio_conf;
switch (state->demod_type) {
case TDA1004X_DEMOD_TDA10045:
@@ -1174,6 +1173,13 @@ static int tda1004x_sleep(struct dvb_frontend* fe)
case TDA1004X_DEMOD_TDA10046:
/* set outputs to tristate */
tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff);
+ /* invert GPIO 1 and 3 if desired*/
+ gpio_conf = state->config->gpio_config;
+ if (gpio_conf >= TDA10046_GP00_I)
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f,
+ (gpio_conf & 0x0f) ^ 0x0a);
+
+ tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0);
tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
break;
}
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
index ec502d71b83c..abae84350142 100644
--- a/drivers/media/dvb/frontends/tda1004x.h
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -35,9 +35,23 @@ enum tda10046_agc {
TDA10046_AGC_DEFAULT, /* original configuration */
TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */
TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */
- TDA10046_AGC_TDA827X_GP11, /* IF AGC only, special setup for tda827x */
- TDA10046_AGC_TDA827X_GP00, /* same as above, but GPIOs 0 */
- TDA10046_AGC_TDA827X_GP01, /* same as above, but GPIO3=0 GPIO1=1*/
+ TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */
+};
+
+/* Many (hybrid) boards use GPIO 1 and 3
+ GPIO1 analog - dvb switch
+ GPIO3 firmware eeprom address switch
+*/
+enum tda10046_gpio {
+ TDA10046_GPTRI = 0x00, /* All GPIOs tristate */
+ TDA10046_GP00 = 0x40, /* GPIO3=0, GPIO1=0 */
+ TDA10046_GP01 = 0x42, /* GPIO3=0, GPIO1=1 */
+ TDA10046_GP10 = 0x48, /* GPIO3=1, GPIO1=0 */
+ TDA10046_GP11 = 0x4a, /* GPIO3=1, GPIO1=1 */
+ TDA10046_GP00_I = 0x80, /* GPIO3=0, GPIO1=0, invert in sleep mode*/
+ TDA10046_GP01_I = 0x82, /* GPIO3=0, GPIO1=1, invert in sleep mode */
+ TDA10046_GP10_I = 0x88, /* GPIO3=1, GPIO1=0, invert in sleep mode */
+ TDA10046_GP11_I = 0x8a, /* GPIO3=1, GPIO1=1, invert in sleep mode */
};
enum tda10046_if {
@@ -47,6 +61,11 @@ enum tda10046_if {
TDA10046_FREQ_052, /* low IF, 5.1667 MHZ for tda9889 */
};
+enum tda10046_tsout {
+ TDA10046_TS_PARALLEL = 0x00, /* parallel transport stream, default */
+ TDA10046_TS_SERIAL = 0x01, /* serial transport stream */
+};
+
struct tda1004x_config
{
/* the demodulator's i2c address */
@@ -58,6 +77,9 @@ struct tda1004x_config
/* Does the OCLK signal need inverted? */
u8 invert_oclk;
+ /* parallel or serial transport stream */
+ enum tda10046_tsout ts_mode;
+
/* Xtal frequency, 4 or 16MHz*/
enum tda10046_xtal xtal_freq;
@@ -67,11 +89,35 @@ struct tda1004x_config
/* AGC configuration */
enum tda10046_agc agc_config;
+ /* setting of GPIO1 and 3 */
+ enum tda10046_gpio gpio_config;
+
+ /* slave address and configuration of the tuner */
+ u8 tuner_address;
+ u8 tuner_config;
+ u8 antenna_switch;
+
+ /* if the board uses another I2c Bridge (tda8290), its address */
+ u8 i2c_gate;
+
/* request firmware for device */
- /* set this to NULL if the card has a firmware EEPROM */
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+enum tda1004x_demod {
+ TDA1004X_DEMOD_TDA10045,
+ TDA1004X_DEMOD_TDA10046,
+};
+
+struct tda1004x_state {
+ struct i2c_adapter* i2c;
+ const struct tda1004x_config* config;
+ struct dvb_frontend frontend;
+
+ /* private demod data */
+ enum tda1004x_demod demod_type;
+};
+
#if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE))
extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
struct i2c_adapter* i2c);
diff --git a/drivers/media/dvb/frontends/tda827x.c b/drivers/media/dvb/frontends/tda827x.c
new file mode 100644
index 000000000000..256fc4bf500b
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda827x.c
@@ -0,0 +1,512 @@
+/*
+ *
+ * (c) 2005 Hartmut Hackmann
+ * (c) 2007 Michael Krufky
+ *
+ * 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 "tda827x.h"
+
+static int debug = 0;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "tda827x: " args); \
+ } while (0)
+
+struct tda827x_priv {
+ int i2c_addr;
+ struct i2c_adapter *i2c_adap;
+ struct tda827x_config *cfg;
+ u32 frequency;
+ u32 bandwidth;
+};
+
+struct tda827x_data {
+ u32 lomax;
+ u8 spd;
+ u8 bs;
+ u8 bp;
+ u8 cp;
+ u8 gc3;
+ u8 div1p5;
+};
+
+static const struct tda827x_data tda827x_dvbt[] = {
+ { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
+ { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
+ { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
+ { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
+ { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
+ { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
+ { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0},
+ { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
+ { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
+ { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
+ { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
+ { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
+ { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
+ { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
+ { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
+ { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
+ { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
+ { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0}
+};
+
+static int tda827xo_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ u8 buf[14];
+
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+ int i, tuner_freq, if_freq;
+ u32 N;
+
+ dprintk("%s:\n", __FUNCTION__);
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ if_freq = 4000000;
+ break;
+ case BANDWIDTH_7_MHZ:
+ if_freq = 4500000;
+ break;
+ default: /* 8 MHz or Auto */
+ if_freq = 5000000;
+ break;
+ }
+ tuner_freq = params->frequency + if_freq;
+
+ i = 0;
+ while (tda827x_dvbt[i].lomax < tuner_freq) {
+ if(tda827x_dvbt[i + 1].lomax == 0)
+ break;
+ i++;
+ }
+
+ N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2);
+ buf[0] = 0;
+ buf[1] = (N>>8) | 0x40;
+ buf[2] = N & 0xff;
+ buf[3] = 0;
+ buf[4] = 0x52;
+ buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) +
+ (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp;
+ buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f;
+ buf[7] = 0xbf;
+ buf[8] = 0x2a;
+ buf[9] = 0x05;
+ buf[10] = 0xff;
+ buf[11] = 0x00;
+ buf[12] = 0x00;
+ buf[13] = 0x40;
+
+ msg.len = 14;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
+ printk("%s: could not write to tuner at addr: 0x%02x\n",
+ __FUNCTION__, priv->i2c_addr << 1);
+ return -EIO;
+ }
+ msleep(500);
+ /* correct CP value */
+ buf[0] = 0x30;
+ buf[1] = 0x50 + tda827x_dvbt[i].cp;
+ msg.len = 2;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ priv->frequency = tuner_freq - if_freq; // FIXME
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+
+ return 0;
+}
+
+static int tda827xo_sleep(struct dvb_frontend *fe)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ static u8 buf[] = { 0x30, 0xd0 };
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+
+ dprintk("%s:\n", __FUNCTION__);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ if (priv->cfg && priv->cfg->sleep)
+ priv->cfg->sleep(fe);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+struct tda827xa_data {
+ u32 lomax;
+ u8 svco;
+ u8 spd;
+ u8 scr;
+ u8 sbs;
+ u8 gc3;
+};
+
+static const struct tda827xa_data tda827xa_dvbt[] = {
+ { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1},
+ { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
+ { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
+ { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
+ { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
+ { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
+ { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
+ { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
+ { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
+ { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0},
+ { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
+};
+
+static int tda827xa_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ u8 buf[11];
+
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+
+ int i, tuner_freq, if_freq;
+ u32 N;
+
+ dprintk("%s:\n", __FUNCTION__);
+ if (priv->cfg && priv->cfg->lna_gain)
+ priv->cfg->lna_gain(fe, 1);
+ msleep(20);
+
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ if_freq = 4000000;
+ break;
+ case BANDWIDTH_7_MHZ:
+ if_freq = 4500000;
+ break;
+ default: /* 8 MHz or Auto */
+ if_freq = 5000000;
+ break;
+ }
+ tuner_freq = params->frequency + if_freq;
+
+ i = 0;
+ while (tda827xa_dvbt[i].lomax < tuner_freq) {
+ if(tda827xa_dvbt[i + 1].lomax == 0)
+ break;
+ i++;
+ }
+
+ N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd;
+ buf[0] = 0; // subaddress
+ buf[1] = N >> 8;
+ buf[2] = N & 0xff;
+ buf[3] = 0;
+ buf[4] = 0x16;
+ buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) +
+ tda827xa_dvbt[i].sbs;
+ buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4);
+ buf[7] = 0x1c;
+ buf[8] = 0x06;
+ buf[9] = 0x24;
+ buf[10] = 0x00;
+ msg.len = 11;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
+ printk("%s: could not write to tuner at addr: 0x%02x\n",
+ __FUNCTION__, priv->i2c_addr << 1);
+ return -EIO;
+ }
+ buf[0] = 0x90;
+ buf[1] = 0xff;
+ buf[2] = 0x60;
+ buf[3] = 0x00;
+ buf[4] = 0x59; // lpsel, for 6MHz + 2
+ msg.len = 5;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ buf[0] = 0xa0;
+ buf[1] = 0x40;
+ msg.len = 2;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(11);
+ msg.flags = I2C_M_RD;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+ msg.flags = 0;
+
+ buf[1] >>= 4;
+ dprintk("tda8275a AGC2 gain is: %d\n", buf[1]);
+ if ((buf[1]) < 2) {
+ if (priv->cfg && priv->cfg->lna_gain)
+ priv->cfg->lna_gain(fe, 0);
+ buf[0] = 0x60;
+ buf[1] = 0x0c;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+ }
+
+ buf[0] = 0xc0;
+ buf[1] = 0x99; // lpsel, for 6MHz + 2
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ buf[0] = 0x60;
+ buf[1] = 0x3c;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ /* correct CP value */
+ buf[0] = 0x30;
+ buf[1] = 0x10 + tda827xa_dvbt[i].scr;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(163);
+ buf[0] = 0xc0;
+ buf[1] = 0x39; // lpsel, for 6MHz + 2
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(3);
+ /* freeze AGC1 */
+ buf[0] = 0x50;
+ buf[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ priv->frequency = tuner_freq - if_freq; // FIXME
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+
+ return 0;
+}
+
+static int tda827xa_sleep(struct dvb_frontend *fe)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ static u8 buf[] = { 0x30, 0x90 };
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+
+ dprintk("%s:\n", __FUNCTION__);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (priv->cfg && priv->cfg->sleep)
+ priv->cfg->sleep(fe);
+
+ return 0;
+}
+
+static int tda827x_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int tda827x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int tda827x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static int tda827x_init(struct dvb_frontend *fe)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ dprintk("%s:\n", __FUNCTION__);
+ if (priv->cfg && priv->cfg->init)
+ priv->cfg->init(fe);
+
+ return 0;
+}
+
+static int tda827x_probe_version(struct dvb_frontend *fe);
+
+static int tda827x_initial_init(struct dvb_frontend *fe)
+{
+ int ret;
+ ret = tda827x_probe_version(fe);
+ if (ret)
+ return ret;
+ return fe->ops.tuner_ops.init(fe);
+}
+
+static int tda827x_initial_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+ ret = tda827x_probe_version(fe);
+ if (ret)
+ return ret;
+ return fe->ops.tuner_ops.sleep(fe);
+}
+
+static struct dvb_tuner_ops tda827xo_tuner_ops = {
+ .info = {
+ .name = "Philips TDA827X",
+ .frequency_min = 55000000,
+ .frequency_max = 860000000,
+ .frequency_step = 250000
+ },
+ .release = tda827x_release,
+ .init = tda827x_initial_init,
+ .sleep = tda827x_initial_sleep,
+ .set_params = tda827xo_set_params,
+ .get_frequency = tda827x_get_frequency,
+ .get_bandwidth = tda827x_get_bandwidth,
+};
+
+static struct dvb_tuner_ops tda827xa_tuner_ops = {
+ .info = {
+ .name = "Philips TDA827XA",
+ .frequency_min = 44000000,
+ .frequency_max = 906000000,
+ .frequency_step = 62500
+ },
+ .release = tda827x_release,
+ .init = tda827x_init,
+ .sleep = tda827xa_sleep,
+ .set_params = tda827xa_set_params,
+ .get_frequency = tda827x_get_frequency,
+ .get_bandwidth = tda827x_get_bandwidth,
+};
+
+static int tda827x_probe_version(struct dvb_frontend *fe)
+{ u8 data;
+ struct tda827x_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD,
+ .buf = &data, .len = 1 };
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
+ printk("%s: could not read from tuner at addr: 0x%02x\n",
+ __FUNCTION__, msg.addr << 1);
+ return -EIO;
+ }
+ if ((data & 0x3c) == 0) {
+ dprintk("tda827x tuner found\n");
+ fe->ops.tuner_ops.init = tda827x_init;
+ fe->ops.tuner_ops.sleep = tda827xo_sleep;
+ } else {
+ dprintk("tda827xa tuner found\n");
+ memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops));
+ }
+ return 0;
+}
+
+struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr,
+ struct i2c_adapter *i2c,
+ struct tda827x_config *cfg)
+{
+ struct tda827x_priv *priv = NULL;
+
+ dprintk("%s:\n", __FUNCTION__);
+ priv = kzalloc(sizeof(struct tda827x_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c_addr = addr;
+ priv->i2c_adap = i2c;
+ priv->cfg = cfg;
+ memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ return fe;
+}
+
+EXPORT_SYMBOL(tda827x_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("DVB TDA827x driver");
+MODULE_AUTHOR("Hartmut Hackmann <hartmut.hackmann@t-online.de>");
+MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
+MODULE_LICENSE("GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/tda827x.h b/drivers/media/dvb/frontends/tda827x.h
new file mode 100644
index 000000000000..69e8263d6d59
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda827x.h
@@ -0,0 +1,62 @@
+ /*
+ DVB Driver for Philips tda827x / tda827xa Silicon tuners
+
+ (c) 2005 Hartmut Hackmann
+ (c) 2007 Michael Krufky
+
+ 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_TDA827X_H__
+#define __DVB_TDA827X_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+struct tda827x_config
+{
+ void (*lna_gain) (struct dvb_frontend *fe, int high);
+ int (*init) (struct dvb_frontend *fe);
+ int (*sleep) (struct dvb_frontend *fe);
+};
+
+
+/**
+ * Attach a tda827x 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 cfg optional callback function pointers.
+ * @return FE pointer on success, NULL on failure.
+ */
+#if defined(CONFIG_DVB_TDA827X) || (defined(CONFIG_DVB_TDA827X_MODULE) && defined(MODULE))
+extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr,
+ struct i2c_adapter *i2c,
+ struct tda827x_config *cfg);
+#else
+static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe,
+ int addr,
+ struct i2c_adapter *i2c,
+ struct tda827x_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA827X
+
+#endif // __DVB_TDA827X_H__
diff --git a/drivers/media/dvb/pluto2/Kconfig b/drivers/media/dvb/pluto2/Kconfig
index 9b84b1bdc313..7d8e6e87bdbb 100644
--- a/drivers/media/dvb/pluto2/Kconfig
+++ b/drivers/media/dvb/pluto2/Kconfig
@@ -2,7 +2,6 @@ config DVB_PLUTO2
tristate "Pluto2 cards"
depends on DVB_CORE && PCI && I2C
select I2C_ALGOBIT
- select DVB_PLL
select DVB_TDA1004X
help
Support for PCI cards based on the Pluto2 FPGA like the Satelco
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index eec7ccf41f8b..7751628e1415 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -3,7 +3,6 @@ config DVB_AV7110
depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
select FW_LOADER if !DVB_AV7110_FIRMWARE
select VIDEO_SAA7146_VV
- select DVB_PLL
select DVB_VES1820 if !DVB_FE_CUSTOMISE
select DVB_VES1X93 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
@@ -62,13 +61,13 @@ config DVB_BUDGET
tristate "Budget cards"
depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
select VIDEO_SAA7146
- select DVB_PLL
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_TDA10023 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
@@ -87,7 +86,6 @@ config DVB_BUDGET_CI
tristate "Budget cards with onboard CI connector"
depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
select VIDEO_SAA7146
- select DVB_PLL
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
@@ -114,6 +112,7 @@ config DVB_BUDGET_AV
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
select DVB_TDA10021 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10023 if !DVB_FE_CUSTOMISE
select DVB_TUA6100 if !DVB_FE_CUSTOMISE
select FW_LOADER
help
@@ -130,7 +129,6 @@ config DVB_BUDGET_PATCH
tristate "AV7110 cards with Budget Patch"
depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1
select DVB_AV7110
- select DVB_PLL
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_VES1X93 if !DVB_FE_CUSTOMISE
select DVB_TDA8083 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 29ed532ba966..67becdd4db60 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -219,7 +219,10 @@ static void recover_arm(struct av7110 *av7110)
av7110->recover(av7110);
restart_feeds(av7110);
- av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config);
+
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+ av7110_check_ir_config(av7110, true);
+#endif
}
static void av7110_arm_sync(struct av7110 *av7110)
@@ -250,6 +253,10 @@ static int arm_thread(void *data)
if (!av7110->arm_ready)
continue;
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+ av7110_check_ir_config(av7110, false);
+#endif
+
if (mutex_lock_interruptible(&av7110->dcomlock))
break;
newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2);
@@ -667,8 +674,8 @@ static void gpioirq(unsigned long data)
return;
case DATA_IRCOMMAND:
- if (av7110->ir_handler)
- av7110->ir_handler(av7110,
+ if (av7110->ir.ir_handler)
+ av7110->ir.ir_handler(av7110,
swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4)));
iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
break;
@@ -1907,8 +1914,10 @@ static int av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status)
if (av7110->fe_synced == synced)
return 0;
- if (av7110->playing)
+ if (av7110->playing) {
+ av7110->fe_synced = synced;
return 0;
+ }
if (mutex_lock_interruptible(&av7110->pid_mutex))
return -ERESTARTSYS;
diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h
index b98bd453cade..115002b0390c 100644
--- a/drivers/media/dvb/ttpci/av7110.h
+++ b/drivers/media/dvb/ttpci/av7110.h
@@ -5,6 +5,7 @@
#include <linux/socket.h>
#include <linux/netdevice.h>
#include <linux/i2c.h>
+#include <linux/input.h>
#include <linux/dvb/video.h>
#include <linux/dvb/audio.h>
@@ -66,6 +67,27 @@ struct dvb_video_events {
};
+struct av7110;
+
+/* infrared remote control */
+struct infrared {
+ u16 key_map[256];
+ struct input_dev *input_dev;
+ char input_phys[32];
+ struct timer_list keyup_timer;
+ struct tasklet_struct ir_tasklet;
+ void (*ir_handler)(struct av7110 *av7110, u32 ircom);
+ u32 ir_command;
+ u32 ir_config;
+ u32 device_mask;
+ u8 protocol;
+ u8 inversion;
+ u16 last_key;
+ u16 last_toggle;
+ u8 delay_timer_finished;
+};
+
+
/* place to store all the necessary device information */
struct av7110 {
@@ -227,10 +249,7 @@ struct av7110 {
u16 wssMode;
u16 wssData;
- u32 ir_config;
- u32 ir_command;
- void (*ir_handler)(struct av7110 *av7110, u32 ircom);
- struct tasklet_struct ir_tasklet;
+ struct infrared ir;
/* firmware stuff */
unsigned char *bin_fw;
@@ -268,6 +287,7 @@ struct av7110 {
extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
u16 subpid, u16 pcrpid);
+extern int av7110_check_ir_config(struct av7110 *av7110, int force);
extern int av7110_ir_init(struct av7110 *av7110);
extern void av7110_ir_exit(struct av7110 *av7110);
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
index e719af807685..654c9e919e04 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -1009,7 +1009,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY)
ret = av7110_av_stop(av7110, RP_VIDEO);
else
- ret = vidcom(av7110, VIDEO_CMD_STOP,
+ ret = vidcom(av7110, AV_VIDEO_CMD_STOP,
av7110->videostate.video_blank ? 0 : 1);
if (!ret)
av7110->trickmode = TRICK_NONE;
@@ -1019,7 +1019,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
av7110->trickmode = TRICK_NONE;
if (av7110->videostate.play_state == VIDEO_FREEZED) {
av7110->videostate.play_state = VIDEO_PLAYING;
- ret = vidcom(av7110, VIDEO_CMD_PLAY, 0);
+ ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
if (ret)
break;
}
@@ -1034,7 +1034,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
ret = av7110_av_start_play(av7110, RP_VIDEO);
}
if (!ret)
- ret = vidcom(av7110, VIDEO_CMD_PLAY, 0);
+ ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
if (!ret)
av7110->videostate.play_state = VIDEO_PLAYING;
break;
@@ -1044,7 +1044,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
if (av7110->playing & RP_VIDEO)
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0);
else
- ret = vidcom(av7110, VIDEO_CMD_FREEZE, 1);
+ ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1);
if (!ret)
av7110->trickmode = TRICK_FREEZE;
break;
@@ -1053,7 +1053,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
if (av7110->playing & RP_VIDEO)
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0);
if (!ret)
- ret = vidcom(av7110, VIDEO_CMD_PLAY, 0);
+ ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
if (!ret) {
av7110->videostate.play_state = VIDEO_PLAYING;
av7110->trickmode = TRICK_NONE;
@@ -1136,7 +1136,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
__Scan_I, 2, AV_PES, 0);
else
- ret = vidcom(av7110, VIDEO_CMD_FFWD, arg);
+ ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg);
if (!ret) {
av7110->trickmode = TRICK_FAST;
av7110->videostate.play_state = VIDEO_PLAYING;
@@ -1147,13 +1147,13 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
if (av7110->playing&RP_VIDEO) {
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0);
if (!ret)
- ret = vidcom(av7110, VIDEO_CMD_SLOW, arg);
+ ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
} else {
- ret = vidcom(av7110, VIDEO_CMD_PLAY, 0);
+ ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
if (!ret)
- ret = vidcom(av7110, VIDEO_CMD_STOP, 0);
+ ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0);
if (!ret)
- ret = vidcom(av7110, VIDEO_CMD_SLOW, arg);
+ ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
}
if (!ret) {
av7110->trickmode = TRICK_SLOW;
@@ -1182,10 +1182,10 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
__Slow, 2, 0, 0);
if (!ret)
- ret = vidcom(av7110, VIDEO_CMD_SLOW, arg);
+ ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
}
if (av7110->trickmode == TRICK_FREEZE)
- ret = vidcom(av7110, VIDEO_CMD_STOP, 1);
+ ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1);
}
break;
diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h
index 4e173c67fbb2..673d9b3f064c 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.h
+++ b/drivers/media/dvb/ttpci/av7110_hw.h
@@ -216,11 +216,11 @@ enum av7110_command_type {
#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */
/* MPEG video decoder commands */
-#define VIDEO_CMD_STOP 0x000e
-#define VIDEO_CMD_PLAY 0x000d
-#define VIDEO_CMD_FREEZE 0x0102
-#define VIDEO_CMD_FFWD 0x0016
-#define VIDEO_CMD_SLOW 0x0022
+#define AV_VIDEO_CMD_STOP 0x000e
+#define AV_VIDEO_CMD_PLAY 0x000d
+#define AV_VIDEO_CMD_FREEZE 0x0102
+#define AV_VIDEO_CMD_FFWD 0x0016
+#define AV_VIDEO_CMD_SLOW 0x0022
/* MPEG audio decoder commands */
#define AUDIO_CMD_MUTE 0x0001
diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c
index f59465bb0af3..a97f166bb523 100644
--- a/drivers/media/dvb/ttpci/av7110_ir.c
+++ b/drivers/media/dvb/ttpci/av7110_ir.c
@@ -1,8 +1,31 @@
+/*
+ * Driver for the remote control of SAA7146 based AV7110 cards
+ *
+ * Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de>
+ * Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/input.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <asm/bitops.h>
@@ -10,18 +33,37 @@
#include "av7110.h"
#include "av7110_hw.h"
-#define UP_TIMEOUT (HZ*7/25)
-/* enable ir debugging by or'ing debug with 16 */
+#define AV_CNT 4
+
+#define IR_RC5 0
+#define IR_RCMM 1
+#define IR_RC5_EXT 2 /* internal only */
+
+#define IR_ALL 0xffffffff
+
+#define UP_TIMEOUT (HZ*7/25)
-static int av_cnt;
-static struct av7110 *av_list[4];
-static struct input_dev *input_dev;
-static char input_phys[32];
-static u8 delay_timer_finished;
+/* Note: enable ir debugging by or'ing debug with 16 */
+
+static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM};
+module_param_array(ir_protocol, int, NULL, 0644);
+MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)");
+
+static int ir_inversion[AV_CNT];
+module_param_array(ir_inversion, int, NULL, 0644);
+MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted");
+
+static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL };
+module_param_array(ir_device_mask, uint, NULL, 0644);
+MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)");
+
+
+static int av_cnt;
+static struct av7110 *av_list[AV_CNT];
-static u16 key_map [256] = {
+static u16 default_key_map [256] = {
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO,
KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -45,141 +87,194 @@ static u16 key_map [256] = {
};
-static void av7110_emit_keyup(unsigned long data)
+/* key-up timer */
+static void av7110_emit_keyup(unsigned long parm)
{
- if (!data || !test_bit(data, input_dev->key))
+ struct infrared *ir = (struct infrared *) parm;
+
+ if (!ir || !test_bit(ir->last_key, ir->input_dev->key))
return;
- input_report_key(input_dev, data, 0);
- input_sync(input_dev);
+ input_report_key(ir->input_dev, ir->last_key, 0);
+ input_sync(ir->input_dev);
}
-static struct timer_list keyup_timer = { .function = av7110_emit_keyup };
-
-
+/* tasklet */
static void av7110_emit_key(unsigned long parm)
{
- struct av7110 *av7110 = (struct av7110 *) parm;
- u32 ir_config = av7110->ir_config;
- u32 ircom = av7110->ir_command;
+ struct infrared *ir = (struct infrared *) parm;
+ u32 ircom = ir->ir_command;
u8 data;
u8 addr;
- static u16 old_toggle = 0;
- u16 new_toggle;
+ u16 toggle;
u16 keycode;
/* extract device address and data */
- switch (ir_config & 0x0003) {
- case 0: /* RC5: 5 bits device address, 6 bits data */
+ switch (ir->protocol) {
+ case IR_RC5: /* RC5: 5 bits device address, 6 bits data */
data = ircom & 0x3f;
addr = (ircom >> 6) & 0x1f;
+ toggle = ircom & 0x0800;
break;
- case 1: /* RCMM: 8(?) bits device address, 8(?) bits data */
+ case IR_RCMM: /* RCMM: ? bits device address, ? bits data */
data = ircom & 0xff;
- addr = (ircom >> 8) & 0xff;
+ addr = (ircom >> 8) & 0x1f;
+ toggle = ircom & 0x8000;
break;
- case 2: /* extended RC5: 5 bits device address, 7 bits data */
+ case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */
data = ircom & 0x3f;
addr = (ircom >> 6) & 0x1f;
/* invert 7th data bit for backward compatibility with RC5 keymaps */
if (!(ircom & 0x1000))
data |= 0x40;
+ toggle = ircom & 0x0800;
break;
default:
- printk("invalid ir_config %x\n", ir_config);
+ printk("%s invalid protocol %x\n", __FUNCTION__, ir->protocol);
return;
}
- keycode = key_map[data];
+ input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data);
+ input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
- dprintk(16, "code %08x -> addr %i data 0x%02x -> keycode %i\n",
- ircom, addr, data, keycode);
+ keycode = ir->key_map[data];
- /* check device address (if selected) */
- if (ir_config & 0x4000)
- if (addr != ((ir_config >> 16) & 0xff))
- return;
+ dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n",
+ __FUNCTION__, ircom, addr, data, keycode);
+
+ /* check device address */
+ if (!(ir->device_mask & (1 << addr)))
+ return;
if (!keycode) {
- printk ("%s: unknown key 0x%02x!!\n", __FUNCTION__, data);
+ printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n",
+ __FUNCTION__, ircom, addr, data);
return;
}
- if ((ir_config & 0x0003) == 1)
- new_toggle = 0; /* RCMM */
- else
- new_toggle = (ircom & 0x800); /* RC5, extended RC5 */
-
- if (timer_pending(&keyup_timer)) {
- del_timer(&keyup_timer);
- if (keyup_timer.data != keycode || new_toggle != old_toggle) {
- delay_timer_finished = 0;
- input_event(input_dev, EV_KEY, keyup_timer.data, 0);
- input_event(input_dev, EV_KEY, keycode, 1);
- input_sync(input_dev);
- } else if (delay_timer_finished) {
- input_event(input_dev, EV_KEY, keycode, 2);
- input_sync(input_dev);
+ if (timer_pending(&ir->keyup_timer)) {
+ del_timer(&ir->keyup_timer);
+ if (ir->last_key != keycode || toggle != ir->last_toggle) {
+ ir->delay_timer_finished = 0;
+ input_event(ir->input_dev, EV_KEY, ir->last_key, 0);
+ input_event(ir->input_dev, EV_KEY, keycode, 1);
+ input_sync(ir->input_dev);
+ } else if (ir->delay_timer_finished) {
+ input_event(ir->input_dev, EV_KEY, keycode, 2);
+ input_sync(ir->input_dev);
}
} else {
- delay_timer_finished = 0;
- input_event(input_dev, EV_KEY, keycode, 1);
- input_sync(input_dev);
+ ir->delay_timer_finished = 0;
+ input_event(ir->input_dev, EV_KEY, keycode, 1);
+ input_sync(ir->input_dev);
}
- keyup_timer.expires = jiffies + UP_TIMEOUT;
- keyup_timer.data = keycode;
+ ir->last_key = keycode;
+ ir->last_toggle = toggle;
- add_timer(&keyup_timer);
+ ir->keyup_timer.expires = jiffies + UP_TIMEOUT;
+ add_timer(&ir->keyup_timer);
- old_toggle = new_toggle;
}
-static void input_register_keys(void)
+
+/* register with input layer */
+static void input_register_keys(struct infrared *ir)
{
int i;
- memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
+ set_bit(EV_KEY, ir->input_dev->evbit);
+ set_bit(EV_REP, ir->input_dev->evbit);
+ set_bit(EV_MSC, ir->input_dev->evbit);
- for (i = 0; i < ARRAY_SIZE(key_map); i++) {
- if (key_map[i] > KEY_MAX)
- key_map[i] = 0;
- else if (key_map[i] > KEY_RESERVED)
- set_bit(key_map[i], input_dev->keybit);
+ set_bit(MSC_RAW, ir->input_dev->mscbit);
+ set_bit(MSC_SCAN, ir->input_dev->mscbit);
+
+ memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
+
+ for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) {
+ if (ir->key_map[i] > KEY_MAX)
+ ir->key_map[i] = 0;
+ else if (ir->key_map[i] > KEY_RESERVED)
+ set_bit(ir->key_map[i], ir->input_dev->keybit);
}
+
+ ir->input_dev->keycode = ir->key_map;
+ ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
+ ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
}
-static void input_repeat_key(unsigned long data)
+/* called by the input driver after rep[REP_DELAY] ms */
+static void input_repeat_key(unsigned long parm)
{
- /* called by the input driver after rep[REP_DELAY] ms */
- delay_timer_finished = 1;
+ struct infrared *ir = (struct infrared *) parm;
+
+ ir->delay_timer_finished = 1;
}
-static int av7110_setup_irc_config(struct av7110 *av7110, u32 ir_config)
+/* check for configuration changes */
+int av7110_check_ir_config(struct av7110 *av7110, int force)
{
- int ret = 0;
+ int i;
+ int modified = force;
+ int ret = -ENODEV;
- dprintk(4, "%p\n", av7110);
- if (av7110) {
- ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, ir_config);
- av7110->ir_config = ir_config;
+ for (i = 0; i < av_cnt; i++)
+ if (av7110 == av_list[i])
+ break;
+
+ if (i < av_cnt && av7110) {
+ if ((av7110->ir.protocol & 1) != ir_protocol[i] ||
+ av7110->ir.inversion != ir_inversion[i])
+ modified = true;
+
+ if (modified) {
+ /* protocol */
+ if (ir_protocol[i]) {
+ ir_protocol[i] = 1;
+ av7110->ir.protocol = IR_RCMM;
+ av7110->ir.ir_config = 0x0001;
+ } else if (FW_VERSION(av7110->arm_app) >= 0x2620) {
+ av7110->ir.protocol = IR_RC5_EXT;
+ av7110->ir.ir_config = 0x0002;
+ } else {
+ av7110->ir.protocol = IR_RC5;
+ av7110->ir.ir_config = 0x0000;
+ }
+ /* inversion */
+ if (ir_inversion[i]) {
+ ir_inversion[i] = 1;
+ av7110->ir.ir_config |= 0x8000;
+ }
+ av7110->ir.inversion = ir_inversion[i];
+ /* update ARM */
+ ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
+ av7110->ir.ir_config);
+ } else
+ ret = 0;
+
+ /* address */
+ if (av7110->ir.device_mask != ir_device_mask[i])
+ av7110->ir.device_mask = ir_device_mask[i];
}
+
return ret;
}
+/* /proc/av7110_ir interface */
static int av7110_ir_write_proc(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
char *page;
- int size = 4 + 256 * sizeof(u16);
u32 ir_config;
+ int size = sizeof ir_config + sizeof av_list[0]->ir.key_map;
int i;
if (count < size)
@@ -194,71 +289,86 @@ static int av7110_ir_write_proc(struct file *file, const char __user *buffer,
return -EFAULT;
}
- memcpy(&ir_config, page, 4);
- memcpy(&key_map, page + 4, 256 * sizeof(u16));
+ memcpy(&ir_config, page, sizeof ir_config);
+
+ for (i = 0; i < av_cnt; i++) {
+ /* keymap */
+ memcpy(av_list[i]->ir.key_map, page + sizeof ir_config,
+ sizeof(av_list[i]->ir.key_map));
+ /* protocol, inversion, address */
+ ir_protocol[i] = ir_config & 0x0001;
+ ir_inversion[i] = ir_config & 0x8000 ? 1 : 0;
+ if (ir_config & 0x4000)
+ ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f);
+ else
+ ir_device_mask[i] = IR_ALL;
+ /* update configuration */
+ av7110_check_ir_config(av_list[i], false);
+ input_register_keys(&av_list[i]->ir);
+ }
vfree(page);
- if (FW_VERSION(av_list[0]->arm_app) >= 0x2620 && !(ir_config & 0x0001))
- ir_config |= 0x0002; /* enable extended RC5 */
- for (i = 0; i < av_cnt; i++)
- av7110_setup_irc_config(av_list[i], ir_config);
- input_register_keys();
return count;
}
+/* interrupt handler */
static void ir_handler(struct av7110 *av7110, u32 ircom)
{
- dprintk(4, "ircommand = %08x\n", ircom);
- av7110->ir_command = ircom;
- tasklet_schedule(&av7110->ir_tasklet);
+ dprintk(4, "ir command = %08x\n", ircom);
+ av7110->ir.ir_command = ircom;
+ tasklet_schedule(&av7110->ir.ir_tasklet);
}
int __devinit av7110_ir_init(struct av7110 *av7110)
{
+ struct input_dev *input_dev;
static struct proc_dir_entry *e;
int err;
if (av_cnt >= ARRAY_SIZE(av_list))
return -ENOSPC;
- av7110_setup_irc_config(av7110, 0x0001);
av_list[av_cnt++] = av7110;
+ av7110_check_ir_config(av7110, true);
- if (av_cnt == 1) {
- init_timer(&keyup_timer);
- keyup_timer.data = 0;
-
- input_dev = input_allocate_device();
- if (!input_dev)
- return -ENOMEM;
-
- snprintf(input_phys, sizeof(input_phys),
- "pci-%s/ir0", pci_name(av7110->dev->pci));
-
- input_dev->name = "DVB on-card IR receiver";
-
- input_dev->phys = input_phys;
- input_dev->id.bustype = BUS_PCI;
- input_dev->id.version = 1;
- if (av7110->dev->pci->subsystem_vendor) {
- input_dev->id.vendor = av7110->dev->pci->subsystem_vendor;
- input_dev->id.product = av7110->dev->pci->subsystem_device;
- } else {
- input_dev->id.vendor = av7110->dev->pci->vendor;
- input_dev->id.product = av7110->dev->pci->device;
- }
- input_dev->cdev.dev = &av7110->dev->pci->dev;
- set_bit(EV_KEY, input_dev->evbit);
- set_bit(EV_REP, input_dev->evbit);
- input_register_keys();
- err = input_register_device(input_dev);
- if (err) {
- input_free_device(input_dev);
- return err;
- }
- input_dev->timer.function = input_repeat_key;
+ init_timer(&av7110->ir.keyup_timer);
+ av7110->ir.keyup_timer.function = av7110_emit_keyup;
+ av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+ av7110->ir.input_dev = input_dev;
+ snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys),
+ "pci-%s/ir0", pci_name(av7110->dev->pci));
+
+ input_dev->name = "DVB on-card IR receiver";
+
+ input_dev->phys = av7110->ir.input_phys;
+ input_dev->id.bustype = BUS_PCI;
+ input_dev->id.version = 2;
+ if (av7110->dev->pci->subsystem_vendor) {
+ input_dev->id.vendor = av7110->dev->pci->subsystem_vendor;
+ input_dev->id.product = av7110->dev->pci->subsystem_device;
+ } else {
+ input_dev->id.vendor = av7110->dev->pci->vendor;
+ input_dev->id.product = av7110->dev->pci->device;
+ }
+ input_dev->cdev.dev = &av7110->dev->pci->dev;
+ /* initial keymap */
+ memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map);
+ input_register_keys(&av7110->ir);
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+ input_dev->timer.function = input_repeat_key;
+ input_dev->timer.data = (unsigned long) &av7110->ir;
+
+ if (av_cnt == 1) {
e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);
if (e) {
e->write_proc = av7110_ir_write_proc;
@@ -266,8 +376,8 @@ int __devinit av7110_ir_init(struct av7110 *av7110)
}
}
- tasklet_init(&av7110->ir_tasklet, av7110_emit_key, (unsigned long) av7110);
- av7110->ir_handler = ir_handler;
+ tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir);
+ av7110->ir.ir_handler = ir_handler;
return 0;
}
@@ -280,8 +390,10 @@ void __devexit av7110_ir_exit(struct av7110 *av7110)
if (av_cnt == 0)
return;
- av7110->ir_handler = NULL;
- tasklet_kill(&av7110->ir_tasklet);
+ del_timer_sync(&av7110->ir.keyup_timer);
+ av7110->ir.ir_handler = NULL;
+ tasklet_kill(&av7110->ir.ir_tasklet);
+
for (i = 0; i < av_cnt; i++)
if (av_list[i] == av7110) {
av_list[i] = av_list[av_cnt-1];
@@ -289,14 +401,13 @@ void __devexit av7110_ir_exit(struct av7110 *av7110)
break;
}
- if (av_cnt == 1) {
- del_timer_sync(&keyup_timer);
+ if (av_cnt == 1)
remove_proc_entry("av7110_ir", NULL);
- input_unregister_device(input_dev);
- }
+
+ input_unregister_device(av7110->ir.input_dev);
av_cnt--;
}
-//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>");
+//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
//MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 3035b224c7a3..0e817d6f1ce5 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -35,7 +35,7 @@
#include "budget.h"
#include "stv0299.h"
-#include "tda10021.h"
+#include "tda1002x.h"
#include "tda1004x.h"
#include "tua6100.h"
#include "dvb-pll.h"
@@ -66,9 +66,6 @@ struct budget_av {
int slot_status;
struct dvb_ca_en50221 ca;
u8 reinitialise_demod:1;
- u8 tda10021_poclkp:1;
- u8 tda10021_ts_enabled;
- int (*tda10021_set_frontend)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
};
static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot);
@@ -234,12 +231,6 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
if (budget_av->reinitialise_demod)
dvb_frontend_reinitialise(budget_av->budget.dvb_frontend);
- /* set tda10021 back to original clock configuration on reset */
- if (budget_av->tda10021_poclkp) {
- tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
- budget_av->tda10021_ts_enabled = 0;
- }
-
return 0;
}
@@ -256,11 +247,6 @@ static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
budget_av->slot_status = SLOTSTATUS_NONE;
- /* set tda10021 back to original clock configuration when cam removed */
- if (budget_av->tda10021_poclkp) {
- tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
- budget_av->tda10021_ts_enabled = 0;
- }
return 0;
}
@@ -276,12 +262,6 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
- /* tda10021 seems to need a different TS clock config when data is routed to the CAM */
- if (budget_av->tda10021_poclkp) {
- tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1);
- budget_av->tda10021_ts_enabled = 1;
- }
-
return 0;
}
@@ -631,37 +611,62 @@ static struct stv0299_config cinergy_1200s_1894_0010_config = {
static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
{
struct budget *budget = (struct budget *) fe->dvb->priv;
- u8 buf[4];
+ u8 buf[6];
struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+ int i;
+#define CU1216_IF 36125000
#define TUNER_MUL 62500
- u32 div = (params->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL;
+ u32 div = (params->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL;
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
- buf[2] = 0x86;
+ buf[2] = 0xce;
buf[3] = (params->frequency < 150000000 ? 0x01 :
params->frequency < 445000000 ? 0x02 : 0x04);
+ buf[4] = 0xde;
+ buf[5] = 0x20;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+ /* wait for the pll lock */
+ msg.flags = I2C_M_RD;
+ msg.len = 1;
+ for (i = 0; i < 20; i++) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40))
+ break;
+ msleep(10);
+ }
+
+ /* switch the charge pump to the lower current */
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = &buf[2];
+ buf[2] &= ~0x40;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
return -EIO;
+
return 0;
}
-static struct tda10021_config philips_cu1216_config = {
+static struct tda1002x_config philips_cu1216_config = {
.demod_address = 0x0c,
+ .invert = 1,
};
-static struct tda10021_config philips_cu1216_config_altaddress = {
+static struct tda1002x_config philips_cu1216_config_altaddress = {
.demod_address = 0x0d,
+ .invert = 0,
};
-
-
-
static int philips_tu1216_tuner_init(struct dvb_frontend *fe)
{
struct budget *budget = (struct budget *) fe->dvb->priv;
@@ -908,41 +913,28 @@ static u8 read_pwm(struct budget_av *budget_av)
return pwm;
}
-#define SUBID_DVBS_KNC1 0x0010
-#define SUBID_DVBS_KNC1_PLUS 0x0011
-#define SUBID_DVBS_TYPHOON 0x4f56
-#define SUBID_DVBS_CINERGY1200 0x1154
-#define SUBID_DVBS_CYNERGY1200N 0x1155
-
-#define SUBID_DVBS_TV_STAR 0x0014
-#define SUBID_DVBS_TV_STAR_CI 0x0016
-#define SUBID_DVBS_EASYWATCH_1 0x001a
-#define SUBID_DVBS_EASYWATCH 0x001e
-#define SUBID_DVBC_EASYWATCH 0x002a
-#define SUBID_DVBC_KNC1 0x0020
-#define SUBID_DVBC_KNC1_PLUS 0x0021
-#define SUBID_DVBC_CINERGY1200 0x1156
-
-#define SUBID_DVBT_KNC1_PLUS 0x0031
-#define SUBID_DVBT_KNC1 0x0030
-#define SUBID_DVBT_CINERGY1200 0x1157
-
-
-static int tda10021_set_frontend(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *p)
-{
- struct budget_av* budget_av = fe->dvb->priv;
- int result;
-
- result = budget_av->tda10021_set_frontend(fe, p);
- if (budget_av->tda10021_ts_enabled) {
- tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1);
- } else {
- tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
- }
-
- return result;
-}
+#define SUBID_DVBS_KNC1 0x0010
+#define SUBID_DVBS_KNC1_PLUS 0x0011
+#define SUBID_DVBS_TYPHOON 0x4f56
+#define SUBID_DVBS_CINERGY1200 0x1154
+#define SUBID_DVBS_CYNERGY1200N 0x1155
+#define SUBID_DVBS_TV_STAR 0x0014
+#define SUBID_DVBS_TV_STAR_CI 0x0016
+#define SUBID_DVBS_EASYWATCH_1 0x001a
+#define SUBID_DVBS_EASYWATCH 0x001e
+
+#define SUBID_DVBC_EASYWATCH 0x002a
+#define SUBID_DVBC_EASYWATCH_MK3 0x002c
+#define SUBID_DVBC_KNC1 0x0020
+#define SUBID_DVBC_KNC1_PLUS 0x0021
+#define SUBID_DVBC_KNC1_MK3 0x0022
+#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023
+#define SUBID_DVBC_CINERGY1200 0x1156
+#define SUBID_DVBC_CINERGY1200_MK3 0x1176
+
+#define SUBID_DVBT_KNC1_PLUS 0x0031
+#define SUBID_DVBT_KNC1 0x0030
+#define SUBID_DVBT_CINERGY1200 0x1157
static void frontend_init(struct budget_av *budget_av)
{
@@ -961,6 +953,7 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBC_KNC1_PLUS:
case SUBID_DVBT_KNC1_PLUS:
case SUBID_DVBC_EASYWATCH:
+ case SUBID_DVBC_KNC1_PLUS_MK3:
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
break;
}
@@ -1017,6 +1010,7 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBC_CINERGY1200:
case SUBID_DVBC_EASYWATCH:
budget_av->reinitialise_demod = 1;
+ budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
&budget_av->budget.i2c_adap,
read_pwm(budget_av));
@@ -1025,9 +1019,20 @@ static void frontend_init(struct budget_av *budget_av)
&budget_av->budget.i2c_adap,
read_pwm(budget_av));
if (fe) {
- budget_av->tda10021_poclkp = 1;
- budget_av->tda10021_set_frontend = fe->ops.set_frontend;
- fe->ops.set_frontend = tda10021_set_frontend;
+ fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
+ }
+ break;
+
+ case SUBID_DVBC_EASYWATCH_MK3:
+ case SUBID_DVBC_CINERGY1200_MK3:
+ case SUBID_DVBC_KNC1_MK3:
+ case SUBID_DVBC_KNC1_PLUS_MK3:
+ budget_av->reinitialise_demod = 1;
+ budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
+ fe = dvb_attach(tda10023_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;
@@ -1260,12 +1265,16 @@ MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR);
MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S);
MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP);
+MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3);
MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
+MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3);
+MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3);
MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C);
+MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3);
MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T);
static struct pci_device_id pci_tbl[] = {
@@ -1279,13 +1288,17 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a),
+ MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c),
MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
+ MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022),
+ MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023),
MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031),
MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154),
MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155),
MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156),
+ MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176),
MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157),
{
.vendor = 0,
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 464feaf1a9ad..4ed4599ce816 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -73,21 +73,15 @@
#define SLOTSTATUS_READY 8
#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
-/* Milliseconds during which key presses are regarded as key repeat and during
- * which the debounce logic is active
+/*
+ * Milliseconds during which a key is regarded as pressed.
+ * If an identical command arrives within this time, the timer will start over.
*/
-#define IR_REPEAT_TIMEOUT 350
+#define IR_KEYPRESS_TIMEOUT 250
/* RC5 device wildcard */
#define IR_DEVICE_ANY 255
-/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two),
- * this setting allows the superflous sequences to be ignored
- */
-static int debounce = 0;
-module_param(debounce, int, 0644);
-MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)");
-
static int rc5_device = -1;
module_param(rc5_device, int, 0644);
MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)");
@@ -99,10 +93,14 @@ MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
struct budget_ci_ir {
struct input_dev *dev;
struct tasklet_struct msp430_irq_tasklet;
+ struct timer_list timer_keyup;
char name[72]; /* 40 + 32 for (struct saa7146_dev).name */
char phys[32];
struct ir_input_state state;
int rc5_device;
+ u32 last_raw;
+ u32 ir_key;
+ bool have_command;
};
struct budget_ci {
@@ -125,13 +123,8 @@ static void msp430_ir_interrupt(unsigned long data)
{
struct budget_ci *budget_ci = (struct budget_ci *) data;
struct input_dev *dev = budget_ci->ir.dev;
- static int bounces = 0;
- int device;
- int toggle;
- static int prev_toggle = -1;
- static u32 ir_key;
- static int state = 0;
u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+ u32 raw;
/*
* The msp430 chip can generate two different bytes, command and device
@@ -143,7 +136,7 @@ static void msp430_ir_interrupt(unsigned long data)
* bytes and one or more device bytes. For the repeated bytes, the
* highest bit (X) is set. The first command byte is always generated
* before the first device byte. Other than that, no specific order
- * seems to apply.
+ * seems to apply. To make life interesting, bytes can also be lost.
*
* Only when we have a command and device byte, a keypress is
* generated.
@@ -152,53 +145,35 @@ static void msp430_ir_interrupt(unsigned long data)
if (ir_debug)
printk("budget_ci: received byte 0x%02x\n", command);
- /* Is this a repeated byte? */
- if (command & 0x80)
- return;
+ /* Remove repeat bit, we use every command */
+ command = command & 0x7f;
/* Is this a RC5 command byte? */
if (command & 0x40) {
- state = 1;
- ir_key = command & 0x3f;
+ budget_ci->ir.have_command = true;
+ budget_ci->ir.ir_key = command & 0x3f;
return;
}
/* It's a RC5 device byte */
- if (!state)
+ if (!budget_ci->ir.have_command)
return;
- state = 0;
- device = command & 0x1f;
- toggle = command & 0x20;
+ budget_ci->ir.have_command = false;
- if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device)
+ if (budget_ci->ir.rc5_device != IR_DEVICE_ANY &&
+ budget_ci->ir.rc5_device != (command & 0x1f))
return;
- /* Ignore repeated key sequences if requested */
- if (toggle == prev_toggle && ir_key == dev->repeat_key &&
- bounces > 0 && timer_pending(&dev->timer)) {
- if (ir_debug)
- printk("budget_ci: debounce logic ignored IR command\n");
- bounces--;
- return;
- }
- prev_toggle = toggle;
-
- /* Are we still waiting for a keyup event? */
- if (del_timer(&dev->timer))
- ir_input_nokey(dev, &budget_ci->ir.state);
-
- /* Generate keypress */
- if (ir_debug)
- printk("budget_ci: generating keypress 0x%02x\n", ir_key);
- ir_input_keydown(dev, &budget_ci->ir.state, ir_key, (ir_key & (command << 8)));
-
- /* Do we want to delay the keyup event? */
- if (debounce) {
- bounces = debounce;
- mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT));
- } else {
+ /* Is this a repeated key sequence? (same device, command, toggle) */
+ raw = budget_ci->ir.ir_key | (command << 8);
+ if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) {
ir_input_nokey(dev, &budget_ci->ir.state);
+ ir_input_keydown(dev, &budget_ci->ir.state,
+ budget_ci->ir.ir_key, raw);
+ budget_ci->ir.last_raw = raw;
}
+
+ mod_timer(&budget_ci->ir.timer_keyup, jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT));
}
static int msp430_ir_init(struct budget_ci *budget_ci)
@@ -271,16 +246,21 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
break;
}
- /* initialise the key-up debounce timeout handler */
- input_dev->timer.function = msp430_ir_keyup;
- input_dev->timer.data = (unsigned long) &budget_ci->ir;
-
+ /* initialise the key-up timeout handler */
+ init_timer(&budget_ci->ir.timer_keyup);
+ budget_ci->ir.timer_keyup.function = msp430_ir_keyup;
+ budget_ci->ir.timer_keyup.data = (unsigned long) &budget_ci->ir;
+ budget_ci->ir.last_raw = 0xffff; /* An impossible value */
error = input_register_device(input_dev);
if (error) {
printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
goto out2;
}
+ /* note: these must be after input_register_device */
+ input_dev->rep[REP_DELAY] = 400;
+ input_dev->rep[REP_PERIOD] = 250;
+
tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt,
(unsigned long) budget_ci);
@@ -304,10 +284,8 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
- if (del_timer(&dev->timer)) {
- ir_input_nokey(dev, &budget_ci->ir.state);
- input_sync(dev);
- }
+ del_timer_sync(&dev->timer);
+ ir_input_nokey(dev, &budget_ci->ir.state);
input_unregister_device(dev);
}
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c
index e15562f81664..6b97dc1e6b65 100644
--- a/drivers/media/dvb/ttpci/budget-core.c
+++ b/drivers/media/dvb/ttpci/budget-core.c
@@ -41,11 +41,14 @@
#define TS_WIDTH (2 * TS_SIZE)
#define TS_WIDTH_ACTIVY TS_SIZE
+#define TS_WIDTH_DVBC TS_SIZE
#define TS_HEIGHT_MASK 0xf00
#define TS_HEIGHT_MASK_ACTIVY 0xc00
+#define TS_HEIGHT_MASK_DVBC 0xe00
#define TS_MIN_BUFSIZE_K 188
#define TS_MAX_BUFSIZE_K 1410
#define TS_MAX_BUFSIZE_K_ACTIVY 564
+#define TS_MAX_BUFSIZE_K_DVBC 1316
#define BUFFER_WARNING_WAIT (30*HZ)
int budget_debug;
@@ -106,6 +109,19 @@ static int start_ts_capture(struct budget *budget)
saa7146_write(dev, MC2, (MASK_10 | MASK_26));
saa7146_write(dev, BRS_CTRL, 0x60000000);
break;
+ case BUDGET_CIN1200C_MK3:
+ case BUDGET_KNC1C_MK3:
+ case BUDGET_KNC1CP_MK3:
+ if (budget->video_port == BUDGET_VIDEO_PORTA) {
+ saa7146_write(dev, DD1_INIT, 0x06000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x00000000);
+ } else {
+ saa7146_write(dev, DD1_INIT, 0x00000600);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x60000000);
+ }
+ break;
default:
if (budget->video_port == BUDGET_VIDEO_PORTA) {
saa7146_write(dev, DD1_INIT, 0x06000200);
@@ -122,7 +138,13 @@ static int start_ts_capture(struct budget *budget)
mdelay(10);
saa7146_write(dev, BASE_ODD3, 0);
- saa7146_write(dev, BASE_EVEN3, 0);
+ if (budget->buffer_size > budget->buffer_height * budget->buffer_width) {
+ // using odd/even buffers
+ saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width);
+ } else {
+ // using a single buffer
+ saa7146_write(dev, BASE_EVEN3, 0);
+ }
saa7146_write(dev, PROT_ADDR3, budget->buffer_size);
saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
@@ -399,11 +421,25 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
budget->card = bi;
budget->dev = (struct saa7146_dev *) dev;
- if (budget->card->type == BUDGET_FS_ACTIVY) {
+ switch(budget->card->type) {
+ case BUDGET_FS_ACTIVY:
budget->buffer_width = TS_WIDTH_ACTIVY;
max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY;
height_mask = TS_HEIGHT_MASK_ACTIVY;
- } else {
+ break;
+
+ case BUDGET_KNC1C:
+ case BUDGET_KNC1CP:
+ case BUDGET_CIN1200C:
+ case BUDGET_KNC1C_MK3:
+ case BUDGET_KNC1CP_MK3:
+ case BUDGET_CIN1200C_MK3:
+ budget->buffer_width = TS_WIDTH_DVBC;
+ max_bufsize = TS_MAX_BUFSIZE_K_DVBC;
+ height_mask = TS_HEIGHT_MASK_DVBC;
+ break;
+
+ default:
budget->buffer_width = TS_WIDTH;
max_bufsize = TS_MAX_BUFSIZE_K;
height_mask = TS_HEIGHT_MASK;
@@ -415,14 +451,22 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
dma_buffer_size = max_bufsize;
budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width;
- budget->buffer_height &= height_mask;
- budget->buffer_size = budget->buffer_height * budget->buffer_width;
+ if (budget->buffer_height > 0xfff) {
+ budget->buffer_height /= 2;
+ budget->buffer_height &= height_mask;
+ budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width;
+ } else {
+ budget->buffer_height &= height_mask;
+ budget->buffer_size = budget->buffer_height * budget->buffer_width;
+ }
budget->buffer_warning_threshold = budget->buffer_size * 80/100;
budget->buffer_warnings = 0;
budget->buffer_warning_time = jiffies;
- dprintk(2, "%s: width = %d, height = %d\n",
- budget->dev->name, budget->buffer_width, budget->buffer_height);
+ dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n",
+ budget->dev->name,
+ budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single",
+ budget->buffer_width, budget->buffer_height);
printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
if ((ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner, &budget->dev->pci->dev)) < 0) {
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
index e8a5c79178e1..d764ffa728b0 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -99,6 +99,9 @@ static struct saa7146_pci_extension_data x_var = { \
#define BUDGET_KNC1CP 12
#define BUDGET_KNC1TP 13
#define BUDGET_TVSTAR 14
+#define BUDGET_CIN1200C_MK3 15
+#define BUDGET_KNC1C_MK3 16
+#define BUDGET_KNC1CP_MK3 17
#define BUDGET_VIDEO_PORTA 0
#define BUDGET_VIDEO_PORTB 1
diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig
index e78ea9227b0e..f546bccdb997 100644
--- a/drivers/media/dvb/ttusb-budget/Kconfig
+++ b/drivers/media/dvb/ttusb-budget/Kconfig
@@ -1,7 +1,6 @@
config DVB_TTUSB_BUDGET
tristate "Technotrend/Hauppauge Nova-USB devices"
depends on DVB_CORE && USB && I2C
- select DVB_PLL
select DVB_CX22700 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
select DVB_VES1820 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index b2e88ad28977..5adc27c3ced9 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -231,129 +231,149 @@ static struct v4l2_queryctrl radio_qctrl[] = {
}
};
-static int rt_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ 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;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
{
struct video_device *dev = video_devdata(file);
- struct rt_device *rt=dev->priv;
+ struct rt_device *rt = dev->priv;
- switch(cmd)
- {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ if (v->index > 0)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ 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*rt_getsigstr(rt);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt = dev->priv;
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
+ rt->curfreq = f->frequency;
+ rt_setfreq(rt, rt->curfreq);
+ return 0;
+}
- 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*rt_getsigstr(rt);
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt = dev->priv;
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = rt->curfreq;
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ }
+ return -EINVAL;
+}
- rt->curfreq = f->frequency;
- rt_setfreq(rt, rt->curfreq);
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt = dev->priv;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = rt->curfreq;
+ 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;
+}
- 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:
- 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;
- }
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt = dev->priv;
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- rt_do_ioctl);
+ 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;
+}
+
+static int vidioc_g_audio (struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
}
-static int rt_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
{
- return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static struct rt_device rtrack_unit;
@@ -362,7 +382,7 @@ static const struct file_operations rtrack_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = rt_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -374,6 +394,18 @@ static struct video_device rtrack_radio=
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &rtrack_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
};
static int __init rtrack_init(void)
diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c
index 74976cba869f..fdf5d6e46eac 100644
--- a/drivers/media/radio/radio-gemtek-pci.c
+++ b/drivers/media/radio/radio-gemtek-pci.c
@@ -192,131 +192,158 @@ static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
}
-static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *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;
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
{
struct video_device *dev = video_devdata(file);
struct gemtek_pci_card *card = dev->priv;
- switch ( cmd ) {
- case VIDIOC_QUERYCAP:
- {
- 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;
-
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ if (v->index > 0)
+ return -EINVAL;
+
+ 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;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_pci_card *card = dev->priv;
- 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 );
+ if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
+ (f->frequency > GEMTEK_PCI_RANGE_HIGH) )
+ return -EINVAL;
+ gemtek_pci_setfrequency(card, f->frequency);
+ card->current_frequency = f->frequency;
+ card->mute = false;
+ return 0;
+}
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_pci_card *card = dev->priv;
- if (v->index > 0)
- return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = card->current_frequency;
+ return 0;
+}
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ }
+ return -EINVAL;
+}
- if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
- (f->frequency > GEMTEK_PCI_RANGE_HIGH) )
- return -EINVAL;
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_pci_card *card = dev->priv;
+ 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;
+}
- gemtek_pci_setfrequency( card, f->frequency );
- card->current_frequency = f->frequency;
- card->mute = false;
- 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->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 v4l_compat_translate_ioctl(inode,file,cmd,arg,
- gemtek_pci_do_ioctl);
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_pci_card *card = dev->priv;
+
+ 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;
}
-static int gemtek_pci_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
{
- return video_usercopy(inode, file, cmd, arg, gemtek_pci_do_ioctl);
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
enum {
@@ -342,7 +369,7 @@ static const struct file_operations gemtek_pci_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = gemtek_pci_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -353,6 +380,18 @@ static struct video_device vdev_template = {
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &gemtek_pci_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
};
static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 36c4be6622c7..b04b6a7fff7c 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -161,137 +161,157 @@ static int gemtek_getsigstr(struct gemtek_device *dev)
return 1; /* signal present */
}
-static int gemtek_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ 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;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
{
struct video_device *dev = video_devdata(file);
- struct gemtek_device *rt=dev->priv;
+ struct gemtek_device *rt = dev->priv;
- switch(cmd)
- {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ if (v->index > 0)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ 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;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_device *rt = dev->priv;
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
+ 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;
+}
- 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);
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_device *rt = dev->priv;
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = rt->curfreq;
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- case VIDIOC_S_FREQUENCY:
- {
- 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 VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ }
+ return -EINVAL;
+}
- f->type = V4L2_TUNER_RADIO;
- f->frequency = rt->curfreq;
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_device *rt = dev->priv;
- 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 v4l_compat_translate_ioctl(inode,file,cmd,arg,
- gemtek_do_ioctl);
+ 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;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_device *rt = dev->priv;
+
+ 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;
}
-static int gemtek_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_g_audio (struct file *file, void *priv,
+ struct v4l2_audio *a)
{
- return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl);
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static struct gemtek_device gemtek_unit;
@@ -300,7 +320,7 @@ static const struct file_operations gemtek_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = gemtek_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -312,6 +332,18 @@ static struct video_device gemtek_radio=
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &gemtek_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
};
static int __init gemtek_init(void)
diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
index e67b7f258029..11f80cacd6ed 100644
--- a/drivers/media/radio/radio-maestro.c
+++ b/drivers/media/radio/radio-maestro.c
@@ -75,8 +75,6 @@ static struct v4l2_queryctrl radio_qctrl[] = {
static int radio_nr = -1;
module_param(radio_nr, int, 0);
-static int radio_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
static void maestro_remove(struct pci_dev *pdev);
@@ -102,18 +100,11 @@ static const struct file_operations maestro_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = radio_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
-static struct video_device maestro_radio = {
- .name = "Maestro radio",
- .type = VID_TYPE_TUNER,
- .hardware = 0,
- .fops = &maestro_fops,
-};
-
struct radio_device {
u16 io, /* base of Maestro card radio io (GPIO_DATA)*/
muted, /* VIDEO_AUDIO_MUTE */
@@ -190,142 +181,153 @@ static void radio_bits_set(struct radio_device *dev, u32 data)
msleep(125);
}
-static inline int radio_function(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *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;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
{
struct video_device *dev = video_devdata(file);
struct radio_device *card = video_get_drvdata(dev);
- switch (cmd) {
- 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);
+ if (v->index > 0)
+ return -EINVAL;
+
+ (void)radio_bits_get(card);
+
+ 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;
+}
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
- 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;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct radio_device *card = video_get_drvdata(dev);
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
+ return -EINVAL;
+ radio_bits_set(card, FREQ2BITS(f->frequency));
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct radio_device *card = video_get_drvdata(dev);
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = BITS2FREQ(radio_bits_get(card));
+ return 0;
+}
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
- return -EINVAL;
- radio_bits_set(card, FREQ2BITS(f->frequency));
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ }
+ return -EINVAL;
+}
- f->type = V4L2_TUNER_RADIO;
- f->frequency = BITS2FREQ(radio_bits_get(card));
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct radio_device *card = video_get_drvdata(dev);
- 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;
- }
- case VIDIOC_S_CTRL:
- {
- 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;
- }
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- radio_function);
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = card->muted;
+ return 0;
}
+ return -EINVAL;
}
-static int radio_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
{
struct video_device *dev = video_devdata(file);
struct radio_device *card = video_get_drvdata(dev);
- int ret;
+ register u16 io = card->io;
+ register u16 omask = inw(io + IO_MASK);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ 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;
+}
- mutex_lock(&card->lock);
- ret = video_usercopy(inode, file, cmd, arg, radio_function);
- mutex_unlock(&card->lock);
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
- return ret;
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static u16 __devinit radio_power_on(struct radio_device *dev)
@@ -352,6 +354,24 @@ static u16 __devinit radio_power_on(struct radio_device *dev)
return (ofreq == radio_bits_get(dev));
}
+static struct video_device maestro_radio = {
+ .name = "Maestro radio",
+ .type = VID_TYPE_TUNER,
+ .fops = &maestro_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+};
+
static int __devinit maestro_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index f6683872251e..9b493b3298cd 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -122,6 +122,26 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq)
return 0;
}
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ 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;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
static int rt_getsigstr(struct rt_device *dev)
{
if (inb(io) & 2) /* bit set = no signal present */
@@ -129,135 +149,136 @@ static int rt_getsigstr(struct rt_device *dev)
return 1; /* signal present */
}
-static int rt_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
{
struct video_device *dev = video_devdata(file);
- struct rt_device *rt=dev->priv;
+ struct rt_device *rt = dev->priv;
- switch(cmd)
- {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ if (v->index > 0)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ 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;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt = dev->priv;
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
+ rt->curfreq = f->frequency;
+ rt_setfreq(rt, rt->curfreq);
+ return 0;
+}
- 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);
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt = dev->priv;
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = rt->curfreq;
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ }
+ return -EINVAL;
+}
- rt->curfreq = f->frequency;
- rt_setfreq(rt, rt->curfreq);
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt = dev->priv;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = rt->curfreq;
+ 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;
+}
- 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) {
- 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 v4l_compat_translate_ioctl(inode,file,cmd,arg,
- rt_do_ioctl);
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt = dev->priv;
+
+ 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;
}
-static int rt_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
{
- return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static struct rt_device rtrack2_unit;
@@ -266,7 +287,7 @@ static const struct file_operations rtrack2_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = rt_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -278,6 +299,18 @@ static struct video_device rtrack2_radio=
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &rtrack2_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
};
static int __init rtrack2_init(void)
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index f4619e4dda4f..dc33f19c0e2c 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -130,137 +130,155 @@ static inline int fmi_getsigstr(struct fmi_device *dev)
return (res & 2) ? 0 : 0xFFFF;
}
-static int fmi_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
{
+ 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;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ int mult;
struct video_device *dev = video_devdata(file);
- struct fmi_device *fmi=dev->priv;
+ struct fmi_device *fmi = dev->priv;
- switch(cmd)
- {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ if (v->index > 0)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
- int mult;
-
- if (v->index > 0)
- return -EINVAL;
-
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- 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->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);
+ strcpy(v->name, "FM");
+ 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->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;
+}
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmi_device *fmi = dev->priv;
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- 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 = (f->frequency/800)*800;
- fmi_setfreq(fmi);
+ 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 = (f->frequency/800)*800;
+ fmi_setfreq(fmi);
+ return 0;
+}
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmi_device *fmi = dev->priv;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = fmi->curfreq;
- if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
- f->frequency /= 1000;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = fmi->curfreq;
+ if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
+ f->frequency /= 1000;
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- 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=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 v4l_compat_translate_ioctl(inode,file,cmd,arg,
- fmi_do_ioctl);
}
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmi_device *fmi = dev->priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = fmi->curvol;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmi_device *fmi = dev->priv;
+
+ 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;
}
-static int fmi_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
{
- return video_usercopy(inode, file, cmd, arg, fmi_do_ioctl);
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static struct fmi_device fmi_unit;
@@ -269,7 +287,7 @@ static const struct file_operations fmi_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = fmi_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -281,6 +299,18 @@ static struct video_device fmi_radio=
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &fmi_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
};
/* ladis: this is my card. does any other types exist? */
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index b96fafe1f9da..e6c125def5cb 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -226,186 +226,204 @@ static int fmr2_setvolume(struct fmr2_device *dev)
return 0;
}
-static int fmr2_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
{
+ 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;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ int mult;
struct video_device *dev = video_devdata(file);
struct fmr2_device *fmr2 = dev->priv;
- debug_print((KERN_DEBUG "freq %ld flags %d vol %d mute %d "
- "stereo %d type %d\n",
- fmr2->curfreq, fmr2->flags, fmr2->curvol, fmr2->mute,
- fmr2->stereo, fmr2->card_type));
- switch(cmd)
- {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ if (v->index > 0)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
- int mult;
-
- if (v->index > 0)
- return -EINVAL;
-
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- 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->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);
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
+ v->rangelow = RSF16_MINFREQ/mult;
+ v->rangehigh = RSF16_MAXFREQ/mult;
+ 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;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- 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 = (f->frequency/200)*200;
-
- /* set card freq (if not muted) */
- if (fmr2->curvol && !fmr2->mute)
- {
- mutex_lock(&lock);
- fmr2_setfreq(fmr2);
- mutex_unlock(&lock);
- }
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmr2_device *fmr2 = dev->priv;
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- 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 = (f->frequency/200)*200;
+
+ /* set card freq (if not muted) */
+ if (fmr2->curvol && !fmr2->mute) {
+ mutex_lock(&lock);
+ fmr2_setfreq(fmr2);
+ mutex_unlock(&lock);
+ }
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmr2_device *fmr2 = dev->priv;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = fmr2->curfreq;
- if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
- f->frequency /= 1000;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = fmr2->curfreq;
+ if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
+ f->frequency /= 1000;
+ return 0;
+}
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+ struct video_device *dev = video_devdata(file);
+ struct fmr2_device *fmr2 = dev->priv;
+
+ 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;
}
- case VIDIOC_QUERYCTRL:
- {
- 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;
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmr2_device *fmr2 = dev->priv;
+
+ 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;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmr2_device *fmr2 = dev->priv;
+
+ 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;
}
- 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);
+ 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;
}
- 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;
- }
+ break;
+ default:
+ return -EINVAL;
+ }
+
#ifdef DEBUG
- if (fmr2->curvol && !fmr2->mute)
- printk(KERN_DEBUG "unmute\n");
- else
- printk(KERN_DEBUG "mute\n");
+ if (fmr2->curvol && !fmr2->mute)
+ printk(KERN_DEBUG "unmute\n");
+ else
+ printk(KERN_DEBUG "mute\n");
#endif
- mutex_lock(&lock);
- if (fmr2->curvol && !fmr2->mute) {
- fmr2_setvolume(fmr2);
- fmr2_setfreq(fmr2);
- } else
- fmr2_mute(fmr2->port);
- mutex_unlock(&lock);
- return (0);
- }
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- fmr2_do_ioctl);
- }
+ mutex_lock(&lock);
+ if (fmr2->curvol && !fmr2->mute) {
+ fmr2_setvolume(fmr2);
+ fmr2_setfreq(fmr2);
+ } else
+ fmr2_mute(fmr2->port);
+ mutex_unlock(&lock);
+ return 0;
}
-static int fmr2_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
- {
- return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl);
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static struct fmr2_device fmr2_unit;
@@ -414,7 +432,7 @@ static const struct file_operations fmr2_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = fmr2_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -426,6 +444,18 @@ static struct video_device fmr2_radio=
. type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &fmr2_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
};
static int __init fmr2_init(void)
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index d59a27accb84..e43acfd7e533 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -205,135 +205,152 @@ static int tt_getsigstr(struct tt_device *dev) /* TODO */
return 1; /* signal present */
}
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ 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;
+}
-/* implement the video4linux api */
-
-static int tt_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
{
struct video_device *dev = video_devdata(file);
- struct tt_device *tt=dev->priv;
+ struct tt_device *tt = dev->priv;
- switch(cmd)
- {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ if (v->index > 0)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ 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*tt_getsigstr(tt);
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct tt_device *tt = dev->priv;
- 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*tt_getsigstr(tt);
+ tt->curfreq = f->frequency;
+ tt_setfreq(tt, tt->curfreq);
+ return 0;
+}
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct tt_device *tt = dev->priv;
- if (v->index > 0)
- return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = tt->curfreq;
+ return 0;
+}
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
- tt->curfreq = f->frequency;
- tt_setfreq(tt, tt->curfreq);
+ 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;
}
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ }
+ return -EINVAL;
+}
- f->type = V4L2_TUNER_RADIO;
- f->frequency = tt->curfreq;
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct tt_device *tt = dev->priv;
- 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:
- 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;
- }
+ 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;
+}
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- tt_do_ioctl);
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct tt_device *tt = dev->priv;
+
+ 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;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
}
-static int tt_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
{
- return video_usercopy(inode, file, cmd, arg, tt_do_ioctl);
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static struct tt_device terratec_unit;
@@ -342,7 +359,7 @@ static const struct file_operations terratec_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = tt_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -354,6 +371,18 @@ static struct video_device terratec_radio=
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &terratec_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
};
static int __init terratec_init(void)
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index 6d7f1e7116ea..c27c629d99df 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -192,144 +192,154 @@ static void tr_setfreq(unsigned long f)
write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
}
-static int tr_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
{
- switch(cmd)
- {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ 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;
+}
- return 0;
- }
- case VIDIOC_G_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.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->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal=tr_getsigstr();
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+ 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->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal = tr_getsigstr();
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
- if (v->index > 0)
- return -EINVAL;
+ return 0;
+}
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ curfreq = f->frequency;
+ tr_setfreq(curfreq);
+ return 0;
+}
- curfreq = f->frequency;
- tr_setfreq(curfreq);
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = curfreq;
+ return 0;
+}
- f->type = V4L2_TUNER_RADIO;
- f->frequency = curfreq;
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- 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=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;
- }
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ 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;
+}
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- tr_do_ioctl);
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ 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;
}
-static int tr_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
{
- return video_usercopy(inode, file, cmd, arg, tr_do_ioctl);
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static const struct file_operations trust_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = tr_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -341,6 +351,18 @@ static struct video_device trust_radio=
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &trust_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
};
static int __init trust_init(void)
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index 3031fef178cb..8ff5a23a9f01 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -93,8 +93,6 @@ static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency);
static void typhoon_mute(struct typhoon_device *dev);
static void typhoon_unmute(struct typhoon_device *dev);
static int typhoon_setvol(struct typhoon_device *dev, int vol);
-static int typhoon_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
static int typhoon_get_info(char *buf, char **start, off_t offset, int len);
#endif
@@ -186,129 +184,148 @@ static int typhoon_setvol(struct typhoon_device *dev, int vol)
return 0;
}
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ 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;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+
+ 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;
+}
-static int typhoon_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
{
struct video_device *dev = video_devdata(file);
struct typhoon_device *typhoon = dev->priv;
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ typhoon->curfreq = f->frequency;
+ typhoon_setfreq(typhoon, typhoon->curfreq);
+ return 0;
+}
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *v = arg;
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct typhoon_device *typhoon = dev->priv;
- if (v->index > 0)
- return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = typhoon->curfreq;
- memset(v,0,sizeof(*v));
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
+ return 0;
+}
- 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 */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+ }
+ return -EINVAL;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct typhoon_device *typhoon = dev->priv;
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = 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;
+}
- typhoon->curfreq = f->frequency;
- typhoon_setfreq(typhoon, typhoon->curfreq);
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_s_ctrl (struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct typhoon_device *typhoon = dev->priv;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = typhoon->curfreq;
+ 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;
+}
- 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=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;
- }
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- typhoon_do_ioctl);
- }
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
}
-static int typhoon_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
{
- return video_usercopy(inode, file, cmd, arg, typhoon_do_ioctl);
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static struct typhoon_device typhoon_unit =
@@ -322,7 +339,7 @@ static const struct file_operations typhoon_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = typhoon_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -334,6 +351,18 @@ static struct video_device typhoon_radio =
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &typhoon_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
};
#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index ec08491fb7c5..a4715901512d 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -230,121 +230,123 @@ static int zol_is_stereo (struct zol_device *dev)
return 0;
}
-static int zol_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ 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;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
{
struct video_device *dev = video_devdata(file);
struct zol_device *zol = dev->priv;
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *v = arg;
- memset(v,0,sizeof(*v));
- 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;
+ if (v->index > 0)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_G_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=(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);
+ strcpy(v->name, "FM");
+ 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;
+}
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *v = arg;
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
- if (v->index > 0)
- return -EINVAL;
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct zol_device *zol = dev->priv;
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ zol->curfreq = f->frequency;
+ zol_setfreq(zol, zol->curfreq);
+ return 0;
+}
- zol->curfreq = f->frequency;
- zol_setfreq(zol, zol->curfreq);
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct video_device *dev = video_devdata(file);
+ struct zol_device *zol = dev->priv;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = zol->curfreq;
+ return 0;
+}
- f->type = V4L2_TUNER_RADIO;
- f->frequency = zol->curfreq;
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ 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;
}
- 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;
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct zol_device *zol = dev->priv;
+
+ 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;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = video_devdata(file);
+ struct zol_device *zol = dev->priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value)
+ zol_mute(zol);
+ else {
+ zol_unmute(zol);
+ zol_setvol(zol,zol->curvol);
}
- 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);
+ 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) {
@@ -356,19 +358,39 @@ static int zol_do_ioctl(struct inode *inode, struct file *file,
zol_setfreq(zol, zol->curfreq);
}
#endif
- return -EINVAL;
- }
+ return -EINVAL;
+}
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- zol_do_ioctl);
- }
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
}
-static int zol_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
{
- return video_usercopy(inode, file, cmd, arg, zol_do_ioctl);
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
}
static struct zol_device zoltrix_unit;
@@ -378,7 +400,7 @@ static const struct file_operations zoltrix_fops =
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
- .ioctl = zol_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -390,6 +412,18 @@ static struct video_device zoltrix_radio =
.type = VID_TYPE_TUNER,
.hardware = 0,
.fops = &zoltrix_fops,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
};
static int __init zoltrix_init(void)
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 7a6105153f23..639e8b6c35b1 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -647,6 +647,8 @@ config VIDEO_HEXIUM_GEMINI
source "drivers/media/video/cx88/Kconfig"
+source "drivers/media/video/ivtv/Kconfig"
+
config VIDEO_M32R_AR
tristate "AR devices"
depends on M32R && VIDEO_V4L1
@@ -761,6 +763,18 @@ source "drivers/media/video/zc0301/Kconfig"
source "drivers/media/video/pwc/Kconfig"
+config USB_ZR364XX
+ tristate "USB ZR364XX Camera support"
+ depends on USB && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to connect this type of camera to your
+ computer's USB port.
+ See <file:Documentation/video4linux/zr364xx.txt> for more info
+ and list of supported cameras.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zr364xx.
+
endmenu # V4L USB devices
endmenu
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 44ccaed40b49..9c2de501612f 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
obj-$(CONFIG_VIDEO_MEYE) += meye.o
obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
obj-$(CONFIG_VIDEO_CX88) += cx88/
+obj-$(CONFIG_VIDEO_IVTV) += ivtv/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
@@ -99,6 +100,7 @@ obj-$(CONFIG_USB_OV511) += ov511.o
obj-$(CONFIG_USB_SE401) += se401.o
obj-$(CONFIG_USB_STV680) += stv680.o
obj-$(CONFIG_USB_W9968CF) += w9968cf.o
+obj-$(CONFIG_USB_ZR364XX) += zr364xx.o
obj-$(CONFIG_USB_SN9C102) += sn9c102/
obj-$(CONFIG_USB_ET61X251) += et61x251/
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 6addc42df045..6b31e50fb951 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -291,6 +291,9 @@ static struct CARD {
{ 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" },
+ { 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" },
+ { 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" },
+
/* likely broken, vendor id doesn't match the other magic views ...
* { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */
@@ -2907,6 +2910,28 @@ struct tvcard bttv_tvcards[] = {
.has_radio = 1,
.has_remote = 1,
},
+ [BTTV_BOARD_SSAI_SECURITY] = {
+ .name = "SSAI Security Video Interface",
+ .video_inputs = 4,
+ .audio_inputs = 0,
+ .tuner = -1,
+ .svhs = -1,
+ .muxsel = { 0, 1, 2, 3 },
+ .tuner_type = -1,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_SSAI_ULTRASOUND] = {
+ .name = "SSAI Ultrasound Video Interface",
+ .video_inputs = 2,
+ .audio_inputs = 0,
+ .tuner = -1,
+ .svhs = 1,
+ .muxsel = { 2, 0, 1, 3 },
+ .tuner_type = -1,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ },
};
static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
@@ -2970,20 +2995,20 @@ void __devinit bttv_idcard(struct bttv *btv)
if (UNSET != audiomux[0]) {
gpiobits = 0;
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i];
gpiobits |= audiomux[i];
}
} else {
gpiobits = audioall;
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
bttv_tvcards[btv->c.type].gpiomux[i] = audioall;
}
}
bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=",
btv->c.nr,bttv_tvcards[btv->c.type].gpiomask);
- for (i = 0; i < 5; i++) {
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
}
printk("\n");
@@ -3638,7 +3663,7 @@ static int __devinit pvr_altera_load(struct bttv *btv, u8 *micro, u32 microlen)
for (n = 0; n < microlen; n++) {
bits = micro[n];
- for ( i = 0 ; i < 8 ; i++ ) {
+ for (i = 0 ; i < 8 ; i++) {
gpio_bits(BTTV_ALT_DCLK,0);
if (bits & 0x01)
gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA);
@@ -3691,7 +3716,7 @@ static void __devinit osprey_eeprom(struct bttv *btv)
/* this might be an antique... check for MMAC label in eeprom */
if ((ee[0]=='M') && (ee[1]=='M') && (ee[2]=='A') && (ee[3]=='C')) {
unsigned char checksum = 0;
- for (i =0; i<21; i++)
+ for (i = 0; i < 21; i++)
checksum += ee[i];
if (checksum != ee[21])
return;
@@ -3703,12 +3728,13 @@ static void __devinit osprey_eeprom(struct bttv *btv)
unsigned short type;
int offset = 4*16;
- for(; offset < 8*16; offset += 16) {
+ for (; offset < 8*16; offset += 16) {
unsigned short checksum = 0;
/* verify the checksum */
- for(i = 0; i<14; i++) checksum += ee[i+offset];
- checksum = ~checksum; /* no idea why */
- if ((((checksum>>8)&0x0FF) == ee[offset+14]) &&
+ for (i = 0; i < 14; i++)
+ checksum += ee[i+offset];
+ checksum = ~checksum; /* no idea why */
+ if ((((checksum>>8)&0x0FF) == ee[offset+14]) &&
((checksum & 0x0FF) == ee[offset+15])) {
break;
}
@@ -3721,7 +3747,6 @@ static void __devinit osprey_eeprom(struct bttv *btv)
type = (ee[offset+4]<<8) | (ee[offset+5]);
switch(type) {
-
/* 848 based */
case 0x0004:
btv->c.type = BTTV_BOARD_OSPREY1x0_848;
@@ -4149,8 +4174,7 @@ static int tea5757_read(struct bttv *btv)
}
dprintk("bttv%d: tea5757:",btv->c.nr);
- for(i = 0; i < 24; i++)
- {
+ for (i = 0; i < 24; i++) {
udelay(5);
bus_high(btv,btv->mbox_clk);
udelay(5);
@@ -4182,8 +4206,7 @@ static int tea5757_write(struct bttv *btv, int value)
dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value);
bus_low(btv,btv->mbox_clk);
bus_high(btv,btv->mbox_we);
- for(i = 0; i < 25; i++)
- {
+ for (i = 0; i < 25; i++) {
if (reg & 0x1000000)
bus_high(btv,btv->mbox_data);
else
@@ -4755,7 +4778,7 @@ static void kodicom4400r_init(struct bttv *btv)
gpio_write(1 << 9); /* reset MUX */
gpio_write(0);
/* Preset camera 0 to the 4 controllers */
- for (ix=0; ix<4; ix++) {
+ for (ix = 0; ix < 4; ix++) {
sw_status[ix] = ix;
kodicom4400r_write(btv, ix, ix, 1);
}
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 5720b77ac9a7..1c38723d3169 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -164,6 +164,24 @@ static ssize_t show_card(struct class_device *cd, char *buf)
static CLASS_DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
/* ----------------------------------------------------------------------- */
+/* dvb auto-load setup */
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ request_module("dvb-bt8xx");
+}
+
+static void request_modules(struct bttv *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
+/* ----------------------------------------------------------------------- */
/* static data */
/* special timing tables from conexant... */
@@ -4769,9 +4787,11 @@ static int __devinit bttv_probe(struct pci_dev *dev,
disclaim_video_lines(btv);
}
- /* add subdevices */
- if (bttv_tvcards[btv->c.type].has_dvb)
+ /* add subdevices and autoload dvb-bt8xx if needed */
+ if (bttv_tvcards[btv->c.type].has_dvb) {
bttv_sub_add_device(&btv->c, "dvb");
+ request_modules(btv);
+ }
bttv_input_init(btv);
diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c
index ba081f6f8c82..84154c26f9c5 100644
--- a/drivers/media/video/bt8xx/bttv-gpio.c
+++ b/drivers/media/video/bt8xx/bttv-gpio.c
@@ -71,7 +71,6 @@ struct bus_type bttv_sub_bus_type = {
.probe = bttv_sub_probe,
.remove = bttv_sub_remove,
};
-EXPORT_SYMBOL(bttv_sub_bus_type);
static void release_sub_device(struct device *dev)
{
@@ -152,7 +151,6 @@ void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits)
btwrite(data,BT848_GPIO_OUT_EN);
spin_unlock_irqrestore(&btv->gpio_lock,flags);
}
-EXPORT_SYMBOL(bttv_gpio_inout);
u32 bttv_gpio_read(struct bttv_core *core)
{
@@ -162,7 +160,6 @@ u32 bttv_gpio_read(struct bttv_core *core)
value = btread(BT848_GPIO_DATA);
return value;
}
-EXPORT_SYMBOL(bttv_gpio_read);
void bttv_gpio_write(struct bttv_core *core, u32 value)
{
@@ -170,7 +167,6 @@ void bttv_gpio_write(struct bttv_core *core, u32 value)
btwrite(value,BT848_GPIO_DATA);
}
-EXPORT_SYMBOL(bttv_gpio_write);
void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits)
{
@@ -185,7 +181,6 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits)
btwrite(data,BT848_GPIO_DATA);
spin_unlock_irqrestore(&btv->gpio_lock,flags);
}
-EXPORT_SYMBOL(bttv_gpio_bits);
/*
* Local variables:
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 62b873076e09..0dfa49b66418 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -412,7 +412,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
unsigned char buf;
int i,rc;
- for (i = 0; i < 128; i++) {
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
c->addr = i;
rc = i2c_master_recv(c,&buf,0);
if (rc < 0)
diff --git a/drivers/media/video/bt8xx/bttv-if.c b/drivers/media/video/bt8xx/bttv-if.c
index 19b564ab0e92..ecf07988cd33 100644
--- a/drivers/media/video/bt8xx/bttv-if.c
+++ b/drivers/media/video/bt8xx/bttv-if.c
@@ -33,32 +33,16 @@
#include "bttvp.h"
-EXPORT_SYMBOL(bttv_get_cardinfo);
EXPORT_SYMBOL(bttv_get_pcidev);
-EXPORT_SYMBOL(bttv_get_id);
EXPORT_SYMBOL(bttv_gpio_enable);
EXPORT_SYMBOL(bttv_read_gpio);
EXPORT_SYMBOL(bttv_write_gpio);
-EXPORT_SYMBOL(bttv_get_gpio_queue);
-EXPORT_SYMBOL(bttv_i2c_call);
/* ----------------------------------------------------------------------- */
/* Exported functions - for other modules which want to access the */
/* gpio ports (IR for example) */
/* see bttv.h for comments */
-int bttv_get_cardinfo(unsigned int card, int *type, unsigned *cardid)
-{
- printk("The bttv_* interface is obsolete and will go away,\n"
- "please use the new, sysfs based interface instead.\n");
- if (card >= bttv_num) {
- return -1;
- }
- *type = bttvs[card].c.type;
- *cardid = bttvs[card].cardid;
- return 0;
-}
-
struct pci_dev* bttv_get_pcidev(unsigned int card)
{
if (card >= bttv_num)
@@ -66,16 +50,6 @@ struct pci_dev* bttv_get_pcidev(unsigned int card)
return bttvs[card].c.pci;
}
-int bttv_get_id(unsigned int card)
-{
- printk("The bttv_* interface is obsolete and will go away,\n"
- "please use the new, sysfs based interface instead.\n");
- if (card >= bttv_num) {
- return -1;
- }
- return bttvs[card].c.type;
-}
-
int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data)
{
@@ -130,28 +104,6 @@ int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data)
return 0;
}
-wait_queue_head_t* bttv_get_gpio_queue(unsigned int card)
-{
- struct bttv *btv;
-
- if (card >= bttv_num) {
- return NULL;
- }
-
- btv = &bttvs[card];
- if (bttvs[card].shutdown) {
- return NULL;
- }
- return &btv->gpioq;
-}
-
-void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg)
-{
- if (card >= bttv_num)
- return;
- bttv_call_i2c_clients(&bttvs[card], cmd, arg);
-}
-
/*
* Local variables:
* c-basic-offset: 8
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
index 5491acbdaf63..f821ba69db99 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -168,6 +168,8 @@
#define BTTV_BOARD_SABRENT_TVFM 0x8e
#define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f
#define BTTV_BOARD_MACHTV_MAGICTV 0x90
+#define BTTV_BOARD_SSAI_SECURITY 0x91
+#define BTTV_BOARD_SSAI_ULTRASOUND 0x92
/* more card-specific defines */
#define PT2254_L_CHANNEL 0x10
@@ -260,17 +262,8 @@ extern int bttv_handle_chipset(struct bttv *btv);
/* this obsolete -- please use the sysfs-based
interface below for new code */
-/* returns card type + card ID (for bt878-based ones)
- for possible values see lines below beginning with #define BTTV_BOARD_UNKNOWN
- returns negative value if error occurred
-*/
-extern int bttv_get_cardinfo(unsigned int card, int *type,
- unsigned int *cardid);
extern struct pci_dev* bttv_get_pcidev(unsigned int card);
-/* obsolete, use bttv_get_cardinfo instead */
-extern int bttv_get_id(unsigned int card);
-
/* sets GPOE register (BT848_GPIO_OUT_EN) to new value:
data | (current_GPOE_value & ~mask)
returns negative value if error occurred
@@ -290,20 +283,6 @@ extern int bttv_read_gpio(unsigned int card, unsigned long *data);
extern int bttv_write_gpio(unsigned int card,
unsigned long mask, unsigned long data);
-/* returns pointer to task queue which can be used as parameter to
- interruptible_sleep_on
- in interrupt handler if BT848_INT_GPINT bit is set - this queue is activated
- (wake_up_interruptible) and following call to the function bttv_read_gpio
- should return new value of GPDATA,
- returns NULL value if error occurred or queue is not available
- WARNING: because there is no buffer for GPIO data, one MUST
- process data ASAP
-*/
-extern wait_queue_head_t* bttv_get_gpio_queue(unsigned int card);
-
-/* call i2c clients
-*/
-extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index ad79b8d53430..8f44f02029be 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -434,6 +434,9 @@ struct bttv {
unsigned int users;
struct bttv_fh init;
+ /* used to make dvb-bt8xx autoloadable */
+ struct work_struct request_module_wk;
+
/* Default (0) and current (1) video capturing and overlay
cropping parameters in bttv_tvnorm.cropcap units. Protected
by bttv.lock. */
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 710c11a68296..96254dbaf625 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -4,6 +4,7 @@
* sensor.
*
* Copyright 2006 One Laptop Per Child Association, Inc.
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
*
* Written by Jonathan Corbet, corbet@lwn.net.
*
@@ -22,6 +23,7 @@
#include <linux/spinlock.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/list.h>
@@ -36,7 +38,7 @@
#include "cafe_ccic-regs.h"
-#define CAFE_VERSION 0x000001
+#define CAFE_VERSION 0x000002
/*
@@ -164,7 +166,7 @@ struct cafe_camera
struct tasklet_struct s_tasklet;
/* Current operating parameters */
- enum v4l2_chip_ident sensor_type; /* Currently ov7670 only */
+ u32 sensor_type; /* Currently ov7670 only */
struct v4l2_pix_format pix_format;
/* Locks */
@@ -704,7 +706,13 @@ static void cafe_ctlr_init(struct cafe_camera *cam)
cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
+ /*
+ * Here we must wait a bit for the controller to come around.
+ */
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
mdelay(5); /* FIXME revisit this */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+
cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN);
/*
@@ -772,9 +780,9 @@ static void cafe_ctlr_power_up(struct cafe_camera *cam)
* Control 1 is power down, set to 0 to operate.
*/
cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
- mdelay(1); /* Marvell says 1ms will do it */
+// mdelay(1); /* Marvell says 1ms will do it */
cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
- mdelay(1); /* Enough? */
+// mdelay(1); /* Enough? */
spin_unlock_irqrestore(&cam->dev_lock, flags);
}
@@ -818,6 +826,7 @@ static int __cafe_cam_reset(struct cafe_camera *cam)
*/
static int cafe_cam_init(struct cafe_camera *cam)
{
+ struct v4l2_chip_ident chip = { V4L2_CHIP_MATCH_I2C_ADDR, 0, 0, 0 };
int ret;
mutex_lock(&cam->s_mutex);
@@ -827,9 +836,11 @@ static int cafe_cam_init(struct cafe_camera *cam)
ret = __cafe_cam_reset(cam);
if (ret)
goto out;
- ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type);
+ chip.match_chip = cam->sensor->addr;
+ ret = __cafe_cam_cmd(cam, VIDIOC_G_CHIP_IDENT, &chip);
if (ret)
goto out;
+ cam->sensor_type = chip.ident;
// if (cam->sensor->addr != OV7xx0_SID) {
if (cam->sensor_type != V4L2_IDENT_OV7670) {
cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr);
@@ -1792,18 +1803,19 @@ static void cafe_frame_tasklet(unsigned long data)
if (list_empty(&cam->sb_avail))
break; /* Leave it valid, hope for better later */
clear_bit(bufno, &cam->flags);
- /*
- * We could perhaps drop the spinlock during this
- * big copy. Something to consider.
- */
sbuf = list_entry(cam->sb_avail.next,
struct cafe_sio_buffer, list);
+ /*
+ * Drop the lock during the big copy. This *should* be safe...
+ */
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
memcpy(sbuf->buffer, cam->dma_bufs[bufno],
cam->pix_format.sizeimage);
sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage;
sbuf->v4lbuf.sequence = cam->buf_seq[bufno];
sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
+ spin_lock_irqsave(&cam->dev_lock, flags);
list_move_tail(&sbuf->list, &cam->sb_full);
}
if (! list_empty(&cam->sb_full))
@@ -2107,6 +2119,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
cam->v4ldev = cafe_v4l_template;
cam->v4ldev.debug = 0;
// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG;
+ cam->v4ldev.dev = &pdev->dev;
ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1);
if (ret)
goto out_smbus;
@@ -2176,10 +2189,52 @@ static void cafe_pci_remove(struct pci_dev *pdev)
}
+#ifdef CONFIG_PM
+/*
+ * Basic power management.
+ */
+static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct cafe_camera *cam = cafe_find_by_pdev(pdev);
+ int ret;
+
+ ret = pci_save_state(pdev);
+ if (ret)
+ return ret;
+ cafe_ctlr_stop_dma(cam);
+ cafe_ctlr_power_down(cam);
+ pci_disable_device(pdev);
+ return 0;
+}
+
+
+static int cafe_pci_resume(struct pci_dev *pdev)
+{
+ struct cafe_camera *cam = cafe_find_by_pdev(pdev);
+ int ret = 0;
+
+ ret = pci_restore_state(pdev);
+ if (ret)
+ return ret;
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ cam_warn(cam, "Unable to re-enable device on resume!\n");
+ return ret;
+ }
+ cafe_ctlr_init(cam);
+ cafe_ctlr_power_up(cam);
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ if (cam->state == S_SPECREAD)
+ cam->state = S_IDLE; /* Don't bother restarting */
+ else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING)
+ ret = cafe_read_setup(cam, cam->state);
+ return ret;
+}
+
+#endif /* CONFIG_PM */
static struct pci_device_id cafe_ids[] = {
- { PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */
{ PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */
{ PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */
{ 0, }
@@ -2192,6 +2247,10 @@ static struct pci_driver cafe_pci_driver = {
.id_table = cafe_ids,
.probe = cafe_pci_probe,
.remove = cafe_pci_remove,
+#ifdef CONFIG_PM
+ .suspend = cafe_pci_suspend,
+ .resume = cafe_pci_resume,
+#endif
};
diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c
index b12cec94f4cc..19711aaf9a3e 100644
--- a/drivers/media/video/cpia_pp.c
+++ b/drivers/media/video/cpia_pp.c
@@ -62,7 +62,6 @@ static int cpia_pp_close(void *privdata);
#define PPCPIA_PARPORT_OFF -2
#define PPCPIA_PARPORT_NONE -1
-#ifdef MODULE
static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
static char *parport[PARPORT_MAX] = {NULL,};
@@ -72,11 +71,6 @@ MODULE_LICENSE("GPL");
module_param_array(parport, charp, NULL, 0);
MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");
-#else
-static int parport_nr[PARPORT_MAX] __initdata =
- {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
-static int parport_ptr = 0;
-#endif
struct pp_cam_entry {
struct pardevice *pdev;
@@ -141,7 +135,6 @@ static void cpia_pp_run_callback(struct work_struct *work)
cam = container_of(work, struct pp_cam_entry, cb_task);
cb_func = cam->cb_func;
cb_data = cam->cb_data;
- work_release(work);
cb_func(cb_data);
}
@@ -682,7 +675,7 @@ static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), vo
if(cam->port->irq != PARPORT_IRQ_NONE) {
cam->cb_func = cb;
cam->cb_data = cbdata;
- INIT_WORK_NAR(&cam->cb_task, cpia_pp_run_callback);
+ INIT_WORK(&cam->cb_task, cpia_pp_run_callback);
} else {
retval = -1;
}
@@ -820,7 +813,7 @@ static struct parport_driver cpia_pp_driver = {
.detach = cpia_pp_detach,
};
-static int cpia_pp_init(void)
+static int __init cpia_pp_init(void)
{
printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER);
@@ -839,8 +832,7 @@ static int cpia_pp_init(void)
return 0;
}
-#ifdef MODULE
-int init_module(void)
+static int __init cpia_init(void)
{
if (parport[0]) {
/* The user gave some parameters. Let's see what they were. */
@@ -867,38 +859,11 @@ int init_module(void)
return cpia_pp_init();
}
-void cleanup_module(void)
+static void __exit cpia_cleanup(void)
{
- parport_unregister_driver (&cpia_pp_driver);
+ parport_unregister_driver(&cpia_pp_driver);
return;
}
-#else /* !MODULE */
-
-static int __init cpia_pp_setup(char *str)
-{
- int err;
-
- if (!strncmp(str, "parport", 7)) {
- int n = simple_strtoul(str + 7, NULL, 10);
- if (parport_ptr < PARPORT_MAX) {
- parport_nr[parport_ptr++] = n;
- } else {
- LOG("too many ports, %s ignored.\n", str);
- }
- } else if (!strcmp(str, "auto")) {
- parport_nr[0] = PPCPIA_PARPORT_AUTO;
- } else if (!strcmp(str, "none")) {
- parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE;
- }
-
- err=cpia_pp_init();
- if (err)
- return err;
-
- return 1;
-}
-
-__setup("cpia_pp=", cpia_pp_setup);
-
-#endif /* !MODULE */
+module_init(cpia_init);
+module_exit(cpia_cleanup);
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index de87247c74ee..a73e285af730 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -28,6 +28,7 @@
#include <linux/i2c-id.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
MODULE_AUTHOR("Martin Vaughan");
@@ -103,6 +104,9 @@ static int cs53l32a_command(struct i2c_client *client, unsigned int cmd,
cs53l32a_write(client, 0x05, (u8) ctrl->value);
break;
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_CS53l32A, 0);
+
case VIDIOC_LOG_STATUS:
{
u8 v = cs53l32a_read(client, 0x01);
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index d60cd5ecf821..88dbdddeec42 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -51,6 +51,7 @@ const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
V4L2_CID_MPEG_AUDIO_EMPHASIS,
V4L2_CID_MPEG_AUDIO_CRC,
+ V4L2_CID_MPEG_AUDIO_MUTE,
V4L2_CID_MPEG_VIDEO_ENCODING,
V4L2_CID_MPEG_VIDEO_ASPECT,
V4L2_CID_MPEG_VIDEO_B_FRAMES,
@@ -60,6 +61,8 @@ const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_VIDEO_BITRATE,
V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
+ V4L2_CID_MPEG_VIDEO_MUTE,
+ V4L2_CID_MPEG_VIDEO_MUTE_YUV,
V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
@@ -71,6 +74,7 @@ const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
0
};
@@ -102,6 +106,9 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params,
case V4L2_CID_MPEG_AUDIO_CRC:
ctrl->value = params->audio_crc;
break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ ctrl->value = params->audio_mute;
+ break;
case V4L2_CID_MPEG_VIDEO_ENCODING:
ctrl->value = params->video_encoding;
break;
@@ -129,6 +136,12 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params,
case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
ctrl->value = params->video_temporal_decimation;
break;
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ ctrl->value = params->video_mute;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV:
+ ctrl->value = params->video_mute_yuv;
+ break;
case V4L2_CID_MPEG_STREAM_TYPE:
ctrl->value = params->stream_type;
break;
@@ -168,6 +181,9 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params,
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
ctrl->value = params->video_chroma_median_filter_bottom;
break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ ctrl->value = params->stream_insert_nav_packets;
+ break;
default:
return -EINVAL;
}
@@ -201,6 +217,9 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params,
case V4L2_CID_MPEG_AUDIO_CRC:
params->audio_crc = ctrl->value;
break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ params->audio_mute = ctrl->value;
+ break;
case V4L2_CID_MPEG_VIDEO_ASPECT:
params->video_aspect = ctrl->value;
break;
@@ -243,6 +262,12 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params,
case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
params->video_temporal_decimation = ctrl->value;
break;
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ params->video_mute = (ctrl->value != 0);
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV:
+ params->video_mute_yuv = ctrl->value;
+ break;
case V4L2_CID_MPEG_STREAM_TYPE:
params->stream_type = ctrl->value;
params->video_encoding =
@@ -290,6 +315,9 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params,
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
params->video_chroma_median_filter_bottom = ctrl->value;
break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ params->stream_insert_nav_packets = ctrl->value;
+ break;
default:
return -EINVAL;
}
@@ -336,6 +364,9 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 ma
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
name = "Median Chroma Filter Minimum";
break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ name = "Insert Navigation Packets";
+ break;
default:
return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
@@ -350,6 +381,12 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 ma
min = 0;
step = 1;
break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
+ min = 0;
+ max = 1;
+ step = 1;
+ break;
default:
qctrl->type = V4L2_CTRL_TYPE_INTEGER;
break;
@@ -505,6 +542,9 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+
default:
return v4l2_ctrl_query_fill_std(qctrl);
@@ -656,6 +696,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
/* stream */
.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
.stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ .stream_insert_nav_packets = 0,
/* audio */
.audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
@@ -665,6 +706,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
.audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
.audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE,
.audio_crc = V4L2_MPEG_AUDIO_CRC_NONE,
+ .audio_mute = 0,
/* video */
.video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
@@ -676,6 +718,8 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
.video_bitrate = 6000000,
.video_bitrate_peak = 8000000,
.video_temporal_decimation = 0,
+ .video_mute = 0,
+ .video_mute_yuv = 0x008080, /* YCbCr value for black */
/* encoding filters */
.video_spatial_filter_mode = V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
@@ -779,6 +823,10 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties);
if (err) return err;
}
+ if (old == NULL || old->audio_mute != new->audio_mute) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, new->audio_mute);
+ if (err) return err;
+ }
if (old == NULL || old->video_bitrate_mode != new->video_bitrate_mode ||
old->video_bitrate != new->video_bitrate ||
old->video_bitrate_peak != new->video_bitrate_peak) {
@@ -826,6 +874,15 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
new->video_temporal_decimation);
if (err) return err;
}
+ if (old == NULL || old->video_mute != new->video_mute ||
+ (new->video_mute && old->video_mute_yuv != new->video_mute_yuv)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, new->video_mute | (new->video_mute_yuv << 8));
+ if (err) return err;
+ }
+ if (old == NULL || old->stream_insert_nav_packets != new->stream_insert_nav_packets) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, 7, new->stream_insert_nav_packets);
+ if (err) return err;
+ }
return 0;
}
@@ -854,18 +911,22 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
int temporal = p->video_temporal_filter;
/* Stream */
- printk(KERN_INFO "%s: Stream: %s\n",
+ printk(KERN_INFO "%s: Stream: %s",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE));
+ if (p->stream_insert_nav_packets)
+ printk(" (with navigation packets)");
+ printk("\n");
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",
+ printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n",
prefix,
p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1),
- p->is_50hz ? 25 : 30);
+ p->is_50hz ? 25 : 30,
+ (p->video_mute) ? " (muted)" : "");
printk(KERN_INFO "%s: Video: %s, %s, %s, %d",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING),
@@ -886,12 +947,13 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
}
/* Audio */
- printk(KERN_INFO "%s: Audio: %s, %s, %s, %s",
+ printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE),
- cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE));
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE),
+ p->audio_mute ? " (muted)" : "");
if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) {
printk(", %s",
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 774d2536555b..1757a588970f 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -35,6 +35,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#include <media/cx25840.h>
#include "cx25840-core.h"
@@ -827,9 +828,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
cx25840_initialize(client, 0);
break;
- case VIDIOC_INT_G_CHIP_IDENT:
- *(enum v4l2_chip_ident *)arg = state->id;
- break;
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev);
default:
return -EINVAL;
@@ -847,7 +847,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
{
struct i2c_client *client;
struct cx25840_state *state;
- enum v4l2_chip_ident id;
+ u32 id;
u16 device_id;
/* Check if the adapter supports the needed features
@@ -902,6 +902,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
state->audmode = V4L2_TUNER_MODE_LANG1;
state->vbi_line_offset = 8;
state->id = id;
+ state->rev = device_id;
i2c_attach_client(client);
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 28049064dd7d..f4b56d2fd6b6 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -43,7 +43,8 @@ struct cx25840_state {
u32 audclk_freq;
int audmode;
int vbi_line_offset;
- enum v4l2_chip_ident id;
+ u32 id;
+ u32 rev;
int is_cx25836;
};
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
index 0e86b9d033ac..e852024a5ea3 100644
--- a/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -17,7 +17,6 @@
#include <linux/module.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <linux/firmware.h>
#include <media/v4l2-common.h>
#include <media/cx25840.h>
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index b2a66ba625f9..0f9d96963618 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -53,7 +53,6 @@ config VIDEO_CX88_DVB
select DVB_OR51132 if !DVB_FE_CUSTOMISE
select DVB_CX22702 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_TUNER_LGH06XF 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
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index e4355fdc3b6d..3956c257556c 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -232,7 +232,8 @@ static void cx8801_aud_irq(snd_cx88_card_t *chip)
cx_write(MO_AUD_INTSTAT, status);
if (debug > 1 || (status & mask & ~0xff))
cx88_print_irqbits(core->name, "irq aud",
- cx88_aud_irqs, status, mask);
+ cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs),
+ status, mask);
/* risc op code error */
if (status & (1 << 16)) {
printk(KERN_WARNING "%s/0: audio risc op code error\n",core->name);
@@ -413,11 +414,9 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
dprintk(1,"Setting buffer\n");
- buf = kmalloc(sizeof(*buf),GFP_KERNEL);
+ buf = kzalloc(sizeof(*buf),GFP_KERNEL);
if (NULL == buf)
return -ENOMEM;
- memset(buf,0,sizeof(*buf));
-
buf->vb.memory = V4L2_MEMORY_MMAP;
buf->vb.width = chip->period_size;
@@ -682,7 +681,7 @@ static int __devinit snd_cx88_create(struct snd_card *card,
return err;
}
- if (!pci_dma_supported(pci,0xffffffff)) {
+ if (!pci_dma_supported(pci,DMA_32BIT_MASK)) {
dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
err = -EIO;
cx88_core_put(core,pci);
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 65e9d8096b74..e61102dc8ad7 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -885,6 +885,12 @@ struct cx88_board cx88_boards[] = {
.input = {{
.type = CX88_VMUX_DVB,
.vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
}},
.mpeg = CX88_MPEG_DVB,
},
@@ -1537,10 +1543,10 @@ struct cx88_subid cx88_subids[] = {
},{
.subvendor = 0x17de,
.subdevice = 0x0840,
- .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
- },{
- .subvendor = 0x1421,
- .subdevice = 0x0305,
+ .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0305,
.card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
},{
.subvendor = 0x18ac,
@@ -1631,6 +1637,10 @@ struct cx88_subid cx88_subids[] = {
.subvendor = 0x0070,
.subdevice = 0x1402,
.card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */
+ .card = CX88_BOARD_KWORLD_DVBS_100,
},
};
const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids);
@@ -1786,7 +1796,7 @@ static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core)
{ 0x03, 0x0C },
};
- for (i = 0; i < 13; i++) {
+ for (i = 0; i < ARRAY_SIZE(init_bufs); i++) {
msg.buf = init_bufs[i];
msg.len = (i != 12 ? 5 : 2);
err = i2c_transfer(&core->i2c_adap, &msg, 1);
@@ -1913,12 +1923,21 @@ void cx88_card_setup(struct cx88_core *core)
if (0 == core->i2c_rc) {
/* enable tuner */
int i;
- static const u8 buffer [] = { 0x10,0x12,0x13,0x04,0x16,0x00,0x14,0x04,0x017,0x00 };
+ static const u8 buffer [][2] = {
+ {0x10,0x12},
+ {0x13,0x04},
+ {0x16,0x00},
+ {0x14,0x04},
+ {0x17,0x00}
+ };
core->i2c_client.addr = 0x0a;
- for (i = 0; i < 5; i++)
- if (2 != i2c_master_send(&core->i2c_client,&buffer[i*2],2))
- printk(KERN_WARNING "%s: Unable to enable tuner(%i).\n",
+ for (i = 0; i < ARRAY_SIZE(buffer); i++)
+ if (2 != i2c_master_send(&core->i2c_client,
+ buffer[i],2))
+ printk(KERN_WARNING
+ "%s: Unable to enable "
+ "tuner(%i).\n",
core->name, i);
}
break;
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index d86813be56de..f31ec96924b9 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -489,12 +489,12 @@ static char *cx88_pci_irqs[32] = {
};
void cx88_print_irqbits(char *name, char *tag, char **strings,
- u32 bits, u32 mask)
+ int len, u32 bits, u32 mask)
{
unsigned int i;
printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
- for (i = 0; i < 32; i++) {
+ for (i = 0; i < len; i++) {
if (!(bits & (1 << i)))
continue;
if (strings[i])
@@ -520,8 +520,8 @@ int cx88_core_irq(struct cx88_core *core, u32 status)
}
if (!handled)
cx88_print_irqbits(core->name, "irq pci",
- cx88_pci_irqs, status,
- core->pci_irqmask);
+ cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
+ status, core->pci_irqmask);
return handled;
}
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 4f5560285770..dbfe4dc9cf8c 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -42,7 +42,6 @@
#include "cx22702.h"
#include "or51132.h"
#include "lgdt330x.h"
-#include "lgh06xf.h"
#include "nxt200x.h"
#include "cx24123.h"
#include "isl6421.h"
@@ -476,6 +475,8 @@ static int dvb_register(struct cx8802_dev *dev)
case CX88_BOARD_WINFAST_DTV2000H:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
dev->dvb.frontend = dvb_attach(cx22702_attach,
&hauppauge_hvr_config,
&dev->core->i2c_adap);
@@ -631,8 +632,9 @@ static int dvb_register(struct cx8802_dev *dev)
&fusionhdtv_5_gold,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_attach(lgh06xf_attach, dev->dvb.frontend,
- &dev->core->i2c_adap);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_lg_tdvs_h06xf);
}
}
break;
@@ -650,8 +652,9 @@ static int dvb_register(struct cx8802_dev *dev)
&pchdtv_hd5500,
&dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_attach(lgh06xf_attach, dev->dvb.frontend,
- &dev->core->i2c_adap);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_lg_tdvs_h06xf);
}
}
break;
@@ -692,24 +695,6 @@ static int dvb_register(struct cx8802_dev *dev)
dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
}
break;
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- dev->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_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;
- case CX88_BOARD_HAUPPAUGE_HVR3000:
- dev->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_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;
default:
printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->core->name);
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index 9830d5c43921..7919a1f9da06 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -1,3 +1,4 @@
+
/*
cx88-i2c.c -- all the i2c code is here
@@ -195,7 +196,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
unsigned char buf;
int i,rc;
- for (i = 0; i < 128; i++) {
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
c->addr = i;
rc = i2c_master_recv(c,&buf,0);
if (rc < 0)
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 1fe1a833c7c7..b2eb32e01aee 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -49,6 +49,27 @@ MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \
printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk);
+
+ if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_DVB)
+ request_module("cx88-dvb");
+ if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_BLACKBIRD)
+ request_module("cx88-blackbird");
+}
+
+static void request_modules(struct cx8802_dev *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
static LIST_HEAD(cx8802_devlist);
/* ------------------------------------------------------------------ */
@@ -345,7 +366,8 @@ static void cx8802_mpeg_irq(struct cx8802_dev *dev)
if (debug || (status & mask & ~0xff))
cx88_print_irqbits(core->name, "irq mpeg ",
- cx88_mpeg_irqs, status, mask);
+ cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),
+ status, mask);
/* risc op code error */
if (status & (1 << 16)) {
@@ -427,7 +449,7 @@ int cx8802_init_common(struct cx8802_dev *dev)
if (pci_enable_device(dev->pci))
return -EIO;
pci_set_master(dev->pci);
- if (!pci_dma_supported(dev->pci,0xffffffff)) {
+ if (!pci_dma_supported(dev->pci,DMA_32BIT_MASK)) {
printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
return -EIO;
}
@@ -778,6 +800,9 @@ static int __devinit cx8802_probe(struct pci_dev *pci_dev,
/* Maintain a reference so cx88-video can query the 8802 device. */
core->dvbdev = dev;
+
+ /* now autoload cx88-dvb or cx88-blackbird */
+ request_modules(dev);
return 0;
fail_free:
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index bdfe2af70124..fbce1d50578b 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -1555,7 +1555,8 @@ static void cx8800_vid_irq(struct cx8800_dev *dev)
cx_write(MO_VID_INTSTAT, status);
if (irq_debug || (status & mask & ~0xff))
cx88_print_irqbits(core->name, "irq vid",
- cx88_vid_irqs, status, mask);
+ cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs),
+ status, mask);
/* risc op code error */
if (status & (1 << 16)) {
@@ -1778,7 +1779,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
pci_set_master(pci_dev);
- if (!pci_dma_supported(pci_dev,0xffffffff)) {
+ if (!pci_dma_supported(pci_dev,DMA_32BIT_MASK)) {
printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name);
err = -EIO;
goto fail_core;
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index a4f7befda5b0..738d4f20c580 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -481,6 +481,8 @@ struct cx8802_dev {
/* List of attached drivers */
struct cx8802_driver drvlist;
+ struct work_struct request_module_wk;
+
};
/* ----------------------------------------------------------- */
@@ -510,7 +512,7 @@ struct cx8802_dev {
/* cx88-core.c */
extern void cx88_print_irqbits(char *name, char *tag, char **strings,
- u32 bits, u32 mask);
+ int len, u32 bits, u32 mask);
extern int cx88_core_irq(struct cx88_core *core, u32 status);
extern void cx88_wakeup(struct cx88_core *core,
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index d829d8f8c1f6..563a8319e608 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -523,7 +523,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
unsigned char buf;
int i, rc;
- for (i = 0; i < 128; i++) {
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
c->addr = i;
rc = i2c_master_recv(c, &buf, 0);
if (rc < 0)
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index 210582d420f8..ed92b6f7187a 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -173,7 +173,7 @@ static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
return -EIO;
}
- for (start = 0; start<4; start++) {
+ for (start = 0; start < ARRAY_SIZE(b); start++) {
if (b[start] == marker) {
code=b[(start+parity_offset+1)%4];
parity=b[(start+parity_offset)%4];
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
new file mode 100644
index 000000000000..e854f3f1b70f
--- /dev/null
+++ b/drivers/media/video/ivtv/Kconfig
@@ -0,0 +1,26 @@
+config VIDEO_IVTV
+ tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support"
+ depends on VIDEO_V4L1 && VIDEO_V4L2 && USB && I2C && EXPERIMENTAL
+ select FW_LOADER
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_CX25840
+ select VIDEO_MSP3400
+ select VIDEO_SAA711X
+ select VIDEO_SAA7127
+ select VIDEO_TVAUDIO
+ select VIDEO_CS53L32A
+ select VIDEO_WM8775
+ select VIDEO_WM8739
+ select VIDEO_UPD64031A
+ select VIDEO_UPD64083
+ ---help---
+ This is a video4linux driver for Conexant cx23416 or cx23416 based
+ PCI personal video recorder devices.
+
+ This is used in devices such as the Hauppauge PVR-150/250/350/500
+ cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ivtv.
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
new file mode 100644
index 000000000000..7e95148fbf4f
--- /dev/null
+++ b/drivers/media/video/ivtv/Makefile
@@ -0,0 +1,7 @@
+ivtv-objs := ivtv-audio.o ivtv-cards.o ivtv-controls.o \
+ ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \
+ ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \
+ ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \
+ ivtv-vbi.o ivtv-video.o ivtv-yuv.o
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
diff --git a/drivers/media/video/ivtv/ivtv-audio.c b/drivers/media/video/ivtv/ivtv-audio.c
new file mode 100644
index 000000000000..d702b8b539a1
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-audio.c
@@ -0,0 +1,74 @@
+/*
+ Audio-related ivtv functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-gpio.h"
+#include "ivtv-cards.h"
+#include "ivtv-audio.h"
+#include <media/msp3400.h>
+#include <linux/videodev.h>
+
+/* Selects the audio input and output according to the current
+ settings. */
+int ivtv_audio_set_io(struct ivtv *itv)
+{
+ struct v4l2_routing route;
+ u32 audio_input;
+ int mux_input;
+
+ /* Determine which input to use */
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+ audio_input = itv->card->radio_input.audio_input;
+ mux_input = itv->card->radio_input.muxer_input;
+ } else {
+ audio_input = itv->card->audio_inputs[itv->audio_input].audio_input;
+ mux_input = itv->card->audio_inputs[itv->audio_input].muxer_input;
+ }
+
+ /* handle muxer chips */
+ route.input = mux_input;
+ route.output = 0;
+ ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+
+ route.input = audio_input;
+ if (itv->card->hw_audio & IVTV_HW_MSP34XX) {
+ route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
+ }
+ return ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+}
+
+void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route)
+{
+ ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
+}
+
+void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq)
+{
+ static u32 freqs[3] = { 44100, 48000, 32000 };
+
+ /* The audio clock of the digitizer must match the codec sample
+ rate otherwise you get some very strange effects. */
+ if (freq > 2)
+ return;
+ ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
+}
+
diff --git a/drivers/media/video/ivtv/ivtv-audio.h b/drivers/media/video/ivtv/ivtv-audio.h
new file mode 100644
index 000000000000..9c42846d8124
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-audio.h
@@ -0,0 +1,23 @@
+/*
+ Audio-related ivtv functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+int ivtv_audio_set_io(struct ivtv *itv);
+void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route);
+void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq);
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
new file mode 100644
index 000000000000..8eab02083887
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -0,0 +1,964 @@
+/*
+ Functions to query card hardware
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-i2c.h"
+
+#include <media/msp3400.h>
+#include <media/wm8775.h>
+#include <media/cs53l32a.h>
+#include <media/cx25840.h>
+#include <media/upd64031a.h>
+
+#define MSP_TUNER MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+ MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER)
+#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+
+/********************** card configuration *******************************/
+
+/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii
+ This keeps the PCI ID database up to date. Note that the entries
+ must be added under vendor 0x4444 (Conexant) as subsystem IDs.
+ New vendor IDs should still be added to the vendor ID list. */
+
+/* Hauppauge PVR-250 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */
+static const struct ivtv_card ivtv_card_pvr250 = {
+ .type = IVTV_CARD_PVR_250,
+ .name = "Hauppauge WinTV PVR-250",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+ IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
+ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-350 cards */
+
+/* Outputs for Hauppauge PVR350 cards */
+static struct ivtv_card_output ivtv_pvr350_outputs[] = {
+ {
+ .name = "S-Video + Composite",
+ .video_output = 0,
+ }, {
+ .name = "Composite",
+ .video_output = 1,
+ }, {
+ .name = "S-Video",
+ .video_output = 2,
+ }, {
+ .name = "RGB",
+ .video_output = 3,
+ }, {
+ .name = "YUV C",
+ .video_output = 4,
+ }, {
+ .name = "YUV V",
+ .video_output = 5,
+ }
+};
+
+static const struct ivtv_card ivtv_card_pvr350 = {
+ .type = IVTV_CARD_PVR_350,
+ .name = "Hauppauge WinTV PVR-350",
+ .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+ .video_outputs = ivtv_pvr350_outputs,
+ .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+ IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
+ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* PVR-350 V1 boards have a different audio tuner input and use a
+ saa7114 instead of a saa7115.
+ Note that the info below comes from a pre-production model so it may
+ not be correct. Especially the audio behaves strangely (mono only it seems) */
+static const struct ivtv_card ivtv_card_pvr350_v1 = {
+ .type = IVTV_CARD_PVR_350_V1,
+ .name = "Hauppauge WinTV PVR-350 (V1)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+ .video_outputs = ivtv_pvr350_outputs,
+ .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+ .hw_video = IVTV_HW_SAA7114,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 |
+ IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, MSP_MONO },
+ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
+ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-150/PVR-500 cards */
+
+static const struct ivtv_card ivtv_card_pvr150 = {
+ .type = IVTV_CARD_PVR_150,
+ .name = "Hauppauge WinTV PVR-150",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_WM8775,
+ .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 |
+ IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 },
+ { IVTV_CARD_INPUT_SVIDEO2, 2, CX25840_SVIDEO2 },
+ { IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER,
+ CX25840_AUDIO8, WM8775_AIN2 },
+ { IVTV_CARD_INPUT_LINE_IN1,
+ CX25840_AUDIO_SERIAL, WM8775_AIN2 },
+ { IVTV_CARD_INPUT_LINE_IN2,
+ CX25840_AUDIO_SERIAL, WM8775_AIN3 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER,
+ CX25840_AUDIO_SERIAL, WM8775_AIN4 },
+ /* apparently needed for the IR blaster */
+ .gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M179 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_m179[] = {
+ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf },
+ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_m179 = {
+ .type = IVTV_CARD_M179,
+ .name = "AVerMedia M179",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7114,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0xe380, .initial_value = 0x8290 },
+ .gpio_audio_input = { .mask = 0x8040, .tuner = 0x8000, .linein = 0x0000 },
+ .gpio_audio_mute = { .mask = 0x2000, .mute = 0x2000 },
+ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
+ .lang1 = 0x0200, .lang2 = 0x0100, .both = 0x0000 },
+ .gpio_audio_freq = { .mask = 0x0018, .f32000 = 0x0000,
+ .f44100 = 0x0008, .f48000 = 0x0010 },
+ .gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 },
+ .tuners = {
+ /* As far as we know all M179 cards use this tuner */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC },
+ },
+ .pci_list = ivtv_pci_m179,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg600 = {
+ .type = IVTV_CARD_MPG600,
+ .name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0x3080, .initial_value = 0x0004 },
+ .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 },
+ .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 },
+ .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004,
+ .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 },
+ .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+ .tuners = {
+ /* The PAL tuner is confirmed */
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_mpg600,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = {
+ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 },
+ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg160 = {
+ .type = IVTV_CARD_MPG160,
+ .name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7114,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0x7080, .initial_value = 0x400c },
+ .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 },
+ .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 },
+ .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004,
+ .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 },
+ .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+ .tuners = {
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_mpg160,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan PG600/Diamond PVR-550 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600 = {
+ .type = IVTV_CARD_PG600,
+ .name = "Yuan PG600, Diamond PVR-550",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .tuners = {
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_pg600,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2410 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2410 = {
+ .type = IVTV_CARD_AVC2410,
+ .name = "Adaptec VideOh! AVC-2410",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_muxer = IVTV_HW_CS53L32A,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A |
+ IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER,
+ MSP_TUNER, CS53L32A_IN0 },
+ { IVTV_CARD_INPUT_LINE_IN1,
+ MSP_SCART1, CS53L32A_IN2 },
+ },
+ /* This card has no eeprom and in fact the Windows driver relies
+ on the country/region setting of the user to decide which tuner
+ is available. */
+ .tuners = {
+ /* This tuner has been verified for the AVC2410 */
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ /* This is a good guess, but I'm not totally sure this is
+ the correct tuner for NTSC. */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+ .pci_list = ivtv_pci_avc2410,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2010 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2010 = {
+ .type = IVTV_CARD_AVC2010,
+ .name = "Adaptec VideOh! AVC-2010",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_CS53L32A,
+ .hw_audio_ctrl = IVTV_HW_CS53L32A,
+ .hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_SVIDEO1, 0, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_LINE_IN1, CS53L32A_IN2 },
+ },
+ /* Does not have a tuner */
+ .pci_list = ivtv_pci_avc2010,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Nagase Transgear 5000TV card */
+
+static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_tg5000tv = {
+ .type = IVTV_CARD_TG5000TV,
+ .name = "Nagase Transgear 5000TV",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+ IVTV_HW_GPIO,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gr_config = UPD64031A_VERTICAL_EXTERNAL,
+ .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+ .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 },
+ .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 },
+ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
+ .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 },
+ .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000,
+ .composite = 0x0010, .svideo = 0x0020 },
+ .tuners = {
+ { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_tg5000tv,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AOpen VA2000MAX-SNT6 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_va2000[] = {
+ { PCI_DEVICE_ID_IVTV16, 0, 0xff5f },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_va2000 = {
+ .type = IVTV_CARD_VA2000MAX_SNT6,
+ .name = "AOpen VA2000MAX-SNT6",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+ IVTV_HW_UPD6408X | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+ },
+ .tuners = {
+ { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_va2000,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc = {
+ .type = IVTV_CARD_CX23416GYC,
+ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .hw_audio = IVTV_HW_SAA717X,
+ .hw_audio_ctrl = IVTV_HW_SAA717X,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO3 |
+ IVTV_SAA717X_TUNER_FLAG },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
+ },
+ .gr_config = UPD64031A_VERTICAL_EXTERNAL,
+ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
+ .composite = 0x0020, .svideo = 0x0020 },
+ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
+ .f44100 = 0x4000, .f48000 = 0x8000 },
+ .tuners = {
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+ .pci_list = ivtv_pci_cx23416gyc,
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
+ .type = IVTV_CARD_CX23416GYC_NOGR,
+ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X,
+ .hw_audio = IVTV_HW_SAA717X,
+ .hw_audio_ctrl = IVTV_HW_SAA717X,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+ IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 |
+ IVTV_SAA717X_TUNER_FLAG },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
+ },
+ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
+ .composite = 0x0020, .svideo = 0x0020 },
+ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
+ .f44100 = 0x4000, .f48000 = 0x8000 },
+ .tuners = {
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
+ .type = IVTV_CARD_CX23416GYC_NOGRYCS,
+ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO,
+ .hw_audio = IVTV_HW_SAA717X,
+ .hw_audio_ctrl = IVTV_HW_SAA717X,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 |
+ IVTV_SAA717X_TUNER_FLAG },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
+ },
+ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
+ .composite = 0x0020, .svideo = 0x0020 },
+ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
+ .f44100 = 0x4000, .f48000 = 0x8000 },
+ .tuners = {
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx = {
+ .type = IVTV_CARD_GV_MVPRX,
+ .name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_WM8739,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TVAUDIO |
+ IVTV_HW_TUNER | IVTV_HW_WM8739 |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+ .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 },
+ .tuners = {
+ /* This card has the Panasonic VP27 tuner */
+ { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
+ },
+ .pci_list = ivtv_pci_gv_mvprx,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX2E card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 },
+ {0, 0, 0}
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx2e = {
+ .type = IVTV_CARD_GV_MVPRX2E,
+ .name = "I/O Data GV-MVP/RX2E",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_WM8739,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+ IVTV_HW_TVAUDIO | IVTV_HW_WM8739,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+ .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 },
+ .tuners = {
+ /* This card has the Panasonic VP27 tuner */
+ { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
+ },
+ .pci_list = ivtv_pci_gv_mvprx2e,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
+ .type = IVTV_CARD_GOTVIEW_PCI_DVD,
+ .name = "GotView PCI DVD",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA717X,
+ .hw_audio = IVTV_HW_SAA717X,
+ .hw_audio_ctrl = IVTV_HW_SAA717X,
+ .hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, /* pin 116 */
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, /* pin 114/109 */
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, /* pin 118 */
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN0 },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN2 },
+ },
+ .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 },
+ .tuners = {
+ /* This card has a Philips FQ1216ME MK3 tuner */
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ },
+ .pci_list = ivtv_pci_gotview_pci_dvd,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD2 Deluxe card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
+ .type = IVTV_CARD_GOTVIEW_PCI_DVD2,
+ .name = "GotView PCI DVD2 Deluxe",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+ .gpio_init = { .direction = 0x0800, .initial_value = 0 },
+ .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
+ .tuners = {
+ /* This card has a Philips FQ1216ME MK5 tuner */
+ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ },
+ .pci_list = ivtv_pci_gotview_pci_dvd2,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC622 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_yuan_mpc622 = {
+ .type = IVTV_CARD_YUAN_MPC622,
+ .name = "Yuan MPC622",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 },
+ .tuners = {
+ /* This card has the TDA8290/TDA8275 tuner chips */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 },
+ },
+ .pci_list = ivtv_pci_yuan_mpc622,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* DIGITAL COWBOY DCT-MTVP1 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_dctmvtvp1 = {
+ .type = IVTV_CARD_DCTMTVP1,
+ .name = "Digital Cowboy DCT-MTVP1",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+ IVTV_HW_GPIO,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+ .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 },
+ .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 },
+ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
+ .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 },
+ .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000,
+ .composite = 0x0010, .svideo = 0x0020},
+ .tuners = {
+ { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_dctmvtvp1,
+};
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef HAVE_XC3028
+
+/* Yuan PG600-2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600v2 = {
+ .type = IVTV_CARD_PG600V2,
+ .name = "Yuan PG600-2, GotView PCI DVD Lite, Club3D ZAP-TV1x01",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 },
+ },
+ .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
+ .pci_list = ivtv_pci_pg600v2,
+};
+#endif
+
+static const struct ivtv_card *ivtv_card_list[] = {
+ &ivtv_card_pvr250,
+ &ivtv_card_pvr350,
+ &ivtv_card_pvr150,
+ &ivtv_card_m179,
+ &ivtv_card_mpg600,
+ &ivtv_card_mpg160,
+ &ivtv_card_pg600,
+ &ivtv_card_avc2410,
+ &ivtv_card_avc2010,
+ &ivtv_card_tg5000tv,
+ &ivtv_card_va2000,
+ &ivtv_card_cx23416gyc,
+ &ivtv_card_gv_mvprx,
+ &ivtv_card_gv_mvprx2e,
+ &ivtv_card_gotview_pci_dvd,
+ &ivtv_card_gotview_pci_dvd2,
+ &ivtv_card_yuan_mpc622,
+ &ivtv_card_dctmvtvp1,
+#ifdef HAVE_XC3028
+ &ivtv_card_pg600v2,
+#endif
+
+ /* Variations of standard cards but with the same PCI IDs.
+ These cards must come last in this list. */
+ &ivtv_card_pvr350_v1,
+ &ivtv_card_cx23416gyc_nogr,
+ &ivtv_card_cx23416gyc_nogrycs,
+};
+
+const struct ivtv_card *ivtv_get_card(u16 index)
+{
+ if (index >= ARRAY_SIZE(ivtv_card_list))
+ return NULL;
+ return ivtv_card_list[index];
+}
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input)
+{
+ const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index;
+ static const char * const input_strs[] = {
+ "Tuner 1",
+ "S-Video 1",
+ "S-Video 2",
+ "Composite 1",
+ "Composite 2",
+ "Composite 3"
+ };
+
+ memset(input, 0, sizeof(*input));
+ if (index >= itv->nof_inputs)
+ return -EINVAL;
+ input->index = index;
+ strcpy(input->name, input_strs[card_input->video_type - 1]);
+ input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ?
+ V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+ input->audioset = (1 << itv->nof_audio_inputs) - 1;
+ input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+ itv->tuner_std : V4L2_STD_ALL;
+ return 0;
+}
+
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output)
+{
+ const struct ivtv_card_output *card_output = itv->card->video_outputs + index;
+
+ memset(output, 0, sizeof(*output));
+ if (index >= itv->card->nof_outputs)
+ return -EINVAL;
+ output->index = index;
+ strcpy(output->name, card_output->name);
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->audioset = 1;
+ output->std = V4L2_STD_ALL;
+ return 0;
+}
+
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio)
+{
+ const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index;
+ static const char * const input_strs[] = {
+ "Tuner 1",
+ "Line In 1",
+ "Line In 2"
+ };
+
+ memset(audio, 0, sizeof(*audio));
+ if (index >= itv->nof_audio_inputs)
+ return -EINVAL;
+ strcpy(audio->name, input_strs[aud_input->audio_type - 1]);
+ audio->index = index;
+ audio->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output)
+{
+ memset(aud_output, 0, sizeof(*aud_output));
+ if (itv->card->video_outputs == NULL || index != 0)
+ return -EINVAL;
+ strcpy(aud_output->name, "A/V Audio Out");
+ return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
new file mode 100644
index 000000000000..15012f88b802
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -0,0 +1,207 @@
+/*
+ Functions to query card hardware
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+/* hardware flags */
+#define IVTV_HW_CX25840 (1 << 0)
+#define IVTV_HW_SAA7115 (1 << 1)
+#define IVTV_HW_SAA7127 (1 << 2)
+#define IVTV_HW_MSP34XX (1 << 3)
+#define IVTV_HW_TUNER (1 << 4)
+#define IVTV_HW_WM8775 (1 << 5)
+#define IVTV_HW_CS53L32A (1 << 6)
+#define IVTV_HW_TVEEPROM (1 << 7)
+#define IVTV_HW_SAA7114 (1 << 8)
+#define IVTV_HW_TVAUDIO (1 << 9)
+#define IVTV_HW_UPD64031A (1 << 10)
+#define IVTV_HW_UPD6408X (1 << 11)
+#define IVTV_HW_SAA717X (1 << 12)
+#define IVTV_HW_WM8739 (1 << 13)
+#define IVTV_HW_GPIO (1 << 14)
+
+#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
+
+/* video inputs */
+#define IVTV_CARD_INPUT_VID_TUNER 1
+#define IVTV_CARD_INPUT_SVIDEO1 2
+#define IVTV_CARD_INPUT_SVIDEO2 3
+#define IVTV_CARD_INPUT_COMPOSITE1 4
+#define IVTV_CARD_INPUT_COMPOSITE2 5
+#define IVTV_CARD_INPUT_COMPOSITE3 6
+
+/* audio inputs */
+#define IVTV_CARD_INPUT_AUD_TUNER 1
+#define IVTV_CARD_INPUT_LINE_IN1 2
+#define IVTV_CARD_INPUT_LINE_IN2 3
+
+#define IVTV_CARD_MAX_VIDEO_INPUTS 6
+#define IVTV_CARD_MAX_AUDIO_INPUTS 3
+#define IVTV_CARD_MAX_TUNERS 2
+
+/* SAA71XX HW inputs */
+#define IVTV_SAA71XX_COMPOSITE0 0
+#define IVTV_SAA71XX_COMPOSITE1 1
+#define IVTV_SAA71XX_COMPOSITE2 2
+#define IVTV_SAA71XX_COMPOSITE3 3
+#define IVTV_SAA71XX_COMPOSITE4 4
+#define IVTV_SAA71XX_COMPOSITE5 5
+#define IVTV_SAA71XX_SVIDEO0 6
+#define IVTV_SAA71XX_SVIDEO1 7
+#define IVTV_SAA71XX_SVIDEO2 8
+#define IVTV_SAA71XX_SVIDEO3 9
+
+/* SAA717X needs to mark the tuner input by ORing with this flag */
+#define IVTV_SAA717X_TUNER_FLAG 0x80
+
+/* Dummy HW input */
+#define IVTV_DUMMY_AUDIO 0
+
+/* GPIO HW inputs */
+#define IVTV_GPIO_TUNER 0
+#define IVTV_GPIO_LINE_IN 1
+
+/* SAA717X HW inputs */
+#define IVTV_SAA717X_IN0 0
+#define IVTV_SAA717X_IN1 1
+#define IVTV_SAA717X_IN2 2
+
+/* V4L2 capability aliases */
+#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \
+ V4L2_CAP_SLICED_VBI_CAPTURE)
+#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \
+ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_POS)
+
+struct ivtv_card_video_input {
+ u8 video_type; /* video input type */
+ u8 audio_index; /* index in ivtv_card_audio_input array */
+ u16 video_input; /* hardware video input */
+};
+
+struct ivtv_card_audio_input {
+ u8 audio_type; /* audio input type */
+ u32 audio_input; /* hardware audio input */
+ u16 muxer_input; /* hardware muxer input for boards with a
+ multiplexer chip */
+};
+
+struct ivtv_card_output {
+ u8 name[32];
+ u16 video_output; /* hardware video output */
+};
+
+struct ivtv_card_pci_info {
+ u16 device;
+ u16 subsystem_vendor;
+ u16 subsystem_device;
+};
+
+/* GPIO definitions */
+
+/* The mask is the set of bits used by the operation */
+
+struct ivtv_gpio_init { /* set initial GPIO DIR and OUT values */
+ u16 direction; /* DIR setting. Leave to 0 if no init is needed */
+ u16 initial_value;
+};
+
+struct ivtv_gpio_video_input { /* select tuner/line in input */
+ u16 mask; /* leave to 0 if not supported */
+ u16 tuner;
+ u16 composite;
+ u16 svideo;
+};
+
+struct ivtv_gpio_audio_input { /* select tuner/line in input */
+ u16 mask; /* leave to 0 if not supported */
+ u16 tuner;
+ u16 linein;
+ u16 radio;
+};
+
+struct ivtv_gpio_audio_mute {
+ u16 mask; /* leave to 0 if not supported */
+ u16 mute; /* set this value to mute, 0 to unmute */
+};
+
+struct ivtv_gpio_audio_mode {
+ u16 mask; /* leave to 0 if not supported */
+ u16 mono; /* set audio to mono */
+ u16 stereo; /* set audio to stereo */
+ u16 lang1; /* set audio to the first language */
+ u16 lang2; /* set audio to the second language */
+ u16 both; /* both languages are output */
+};
+
+struct ivtv_gpio_audio_freq {
+ u16 mask; /* leave to 0 if not supported */
+ u16 f32000;
+ u16 f44100;
+ u16 f48000;
+};
+
+struct ivtv_gpio_audio_detect {
+ u16 mask; /* leave to 0 if not supported */
+ u16 stereo; /* if the input matches this value then
+ stereo is detected */
+};
+
+struct ivtv_card_tuner {
+ v4l2_std_id std; /* standard for which the tuner is suitable */
+ int tuner; /* tuner ID (from tuner.h) */
+};
+
+/* for card information/parameters */
+struct ivtv_card {
+ int type;
+ char *name;
+ u32 v4l2_capabilities;
+ u32 hw_video; /* hardware used to process video */
+ u32 hw_audio; /* hardware used to process audio */
+ u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only 1 dev allowed) */
+ u32 hw_muxer; /* hardware used to multiplex audio input */
+ u32 hw_all; /* all hardware used by the board */
+ struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS];
+ struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS];
+ struct ivtv_card_audio_input radio_input;
+ int nof_outputs;
+ const struct ivtv_card_output *video_outputs;
+ u8 gr_config; /* config byte for the ghost reduction device */
+
+ /* GPIO card-specific settings */
+ struct ivtv_gpio_init gpio_init;
+ struct ivtv_gpio_video_input gpio_video_input;
+ struct ivtv_gpio_audio_input gpio_audio_input;
+ struct ivtv_gpio_audio_mute gpio_audio_mute;
+ struct ivtv_gpio_audio_mode gpio_audio_mode;
+ struct ivtv_gpio_audio_freq gpio_audio_freq;
+ struct ivtv_gpio_audio_detect gpio_audio_detect;
+
+ struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS];
+
+ /* list of device and subsystem vendor/devices that
+ correspond to this card type. */
+ const struct ivtv_card_pci_info *pci_list;
+};
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input);
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output);
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input);
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output);
+const struct ivtv_card *ivtv_get_card(u16 index);
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
new file mode 100644
index 000000000000..7a876c3e5b19
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -0,0 +1,303 @@
+/*
+ ioctl control functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-audio.h"
+#include "ivtv-i2c.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-controls.h"
+
+static const u32 user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_AUDIO_BALANCE,
+ V4L2_CID_AUDIO_BASS,
+ V4L2_CID_AUDIO_TREBLE,
+ V4L2_CID_AUDIO_MUTE,
+ V4L2_CID_AUDIO_LOUDNESS,
+ 0
+};
+
+static const u32 *ctrl_classes[] = {
+ user_ctrls,
+ cx2341x_mpeg_ctrls,
+ NULL
+};
+
+static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl)
+{
+ const char *name;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
+
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (qctrl->id == 0)
+ return -EINVAL;
+
+ switch (qctrl->id) {
+ /* Standard V4L2 controls */
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+ if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+
+ default:
+ if (cx2341x_ctrl_query(&itv->params, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+ }
+ strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
+ qctrl->name[sizeof(qctrl->name) - 1] = 0;
+ return 0;
+}
+
+static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu)
+{
+ struct v4l2_queryctrl qctrl;
+
+ qctrl.id = qmenu->id;
+ ivtv_queryctrl(itv, &qctrl);
+ return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+}
+
+static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+{
+ s32 v = vctrl->value;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
+
+ switch (vctrl->id) {
+ /* Standard V4L2 controls */
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+ return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl);
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+
+ default:
+ IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+{
+ IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
+
+ switch (vctrl->id) {
+ /* Standard V4L2 controls */
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+ return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl);
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+ default:
+ IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
+{
+ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
+ return -EINVAL;
+ if (atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
+
+ /* First try to allocate sliced VBI buffers if needed. */
+ if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
+ int i;
+
+ for (i = 0; i < IVTV_VBI_FRAMES; i++) {
+ /* Yuck, hardcoded. Needs to be a define */
+ itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
+ if (itv->vbi.sliced_mpeg_data[i] == NULL) {
+ while (--i >= 0) {
+ kfree(itv->vbi.sliced_mpeg_data[i]);
+ itv->vbi.sliced_mpeg_data[i] = NULL;
+ }
+ return -ENOMEM;
+ }
+ }
+ }
+
+ itv->vbi.insert_mpeg = fmt;
+
+ if (itv->vbi.insert_mpeg == 0) {
+ return 0;
+ }
+ /* Need sliced data for mpeg insertion */
+ if (get_service_set(itv->vbi.sliced_in) == 0) {
+ if (itv->is_60hz)
+ itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
+ else
+ itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+ expand_service_set(itv->vbi.sliced_in, itv->is_50hz);
+ }
+ return 0;
+}
+
+int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ struct v4l2_control ctrl;
+
+ switch (cmd) {
+ case VIDIOC_QUERYMENU:
+ IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
+ return ivtv_querymenu(itv, arg);
+
+ case VIDIOC_QUERYCTRL:
+ return ivtv_queryctrl(itv, arg);
+
+ case VIDIOC_S_CTRL:
+ return ivtv_s_ctrl(itv, arg);
+
+ case VIDIOC_G_CTRL:
+ return ivtv_g_ctrl(itv, arg);
+
+ case VIDIOC_S_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *c = arg;
+
+ if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+ int i;
+ int err = 0;
+
+ for (i = 0; i < c->count; i++) {
+ ctrl.id = c->controls[i].id;
+ ctrl.value = c->controls[i].value;
+ err = ivtv_s_ctrl(itv, &ctrl);
+ c->controls[i].value = ctrl.value;
+ if (err) {
+ c->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+ IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
+ if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ struct cx2341x_mpeg_params p = itv->params;
+ int err = cx2341x_ext_ctrls(&p, arg, cmd);
+
+ if (err)
+ return err;
+
+ if (p.video_encoding != itv->params.video_encoding) {
+ int is_mpeg1 = p.video_encoding ==
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+ struct v4l2_format fmt;
+
+ /* fix videodecoder resolution */
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
+ fmt.fmt.pix.height = itv->params.height;
+ itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
+ }
+ err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
+ if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) {
+ err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
+ }
+ itv->params = p;
+ itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
+ ivtv_audio_set_audio_clock_freq(itv, p.audio_properties & 0x03);
+ return err;
+ }
+ return -EINVAL;
+ }
+
+ case VIDIOC_G_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *c = arg;
+
+ if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+ int i;
+ int err = 0;
+
+ for (i = 0; i < c->count; i++) {
+ ctrl.id = c->controls[i].id;
+ ctrl.value = c->controls[i].value;
+ err = ivtv_g_ctrl(itv, &ctrl);
+ c->controls[i].value = ctrl.value;
+ if (err) {
+ c->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+ IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
+ if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+ return cx2341x_ext_ctrls(&itv->params, arg, cmd);
+ return -EINVAL;
+ }
+
+ case VIDIOC_TRY_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *c = arg;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
+ if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+ return cx2341x_ext_ctrls(&itv->params, arg, cmd);
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h
new file mode 100644
index 000000000000..5a11149725ad
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-controls.h
@@ -0,0 +1,21 @@
+/*
+ ioctl control functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
new file mode 100644
index 000000000000..45b9328a538f
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -0,0 +1,1374 @@
+/*
+ ivtv driver initialization and card probing
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+/* Main Driver file for the ivtv project:
+ * Driver for the Conexant CX23415/CX23416 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ *
+ * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta <alpha292@bremen.or.jp>
+ * using information from T.Adachi,Takeru KOMORIYA and others :-)
+ *
+ * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX
+ * version by T.Adachi. Special thanks Mr.Suzuki
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-firmware.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-streams.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-vbi.h"
+#include "ivtv-audio.h"
+#include "ivtv-gpio.h"
+#include "ivtv-yuv.h"
+
+#include <linux/vermagic.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+
+/* var to keep track of the number of array elements in use */
+int ivtv_cards_active = 0;
+
+/* If you have already X v4l cards, then set this to X. This way
+ the device numbers stay matched. Example: you have a WinTV card
+ without radio and a PVR-350 with. Normally this would give a
+ video1 device together with a radio0 device for the PVR. By
+ setting this to 1 you ensure that radio0 is now also radio1. */
+int ivtv_first_minor = 0;
+
+/* Master variable for all ivtv info */
+struct ivtv *ivtv_cards[IVTV_MAX_CARDS];
+
+/* Protects ivtv_cards_active */
+spinlock_t ivtv_cards_lock = SPIN_LOCK_UNLOCKED;
+
+/* add your revision and whatnot here */
+static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
+ {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+
+const u32 yuv_offset[4] = {
+ IVTV_YUV_BUFFER_OFFSET,
+ IVTV_YUV_BUFFER_OFFSET_1,
+ IVTV_YUV_BUFFER_OFFSET_2,
+ IVTV_YUV_BUFFER_OFFSET_3
+};
+
+/* Parameter declarations */
+static int cardtype[IVTV_MAX_CARDS];
+static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static int cardtype_c = 1;
+static int tuner_c = 1;
+static int radio_c = 1;
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS;
+static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS;
+static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS;
+static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS;
+
+static int ivtv_yuv_mode = 0;
+static int ivtv_yuv_threshold=-1;
+static int ivtv_pci_latency = 1;
+
+int ivtv_debug = 0;
+
+static int newi2c = -1;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, bool, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug,ivtv_debug, int, 0644);
+module_param(ivtv_pci_latency, int, 0644);
+module_param(ivtv_yuv_mode, int, 0644);
+module_param(ivtv_yuv_threshold, int, 0644);
+module_param(ivtv_first_minor, int, 0644);
+
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+module_param(dec_mpg_buffers, int, 0644);
+module_param(dec_yuv_buffers, int, 0644);
+module_param(dec_vbi_buffers, int, 0644);
+
+module_param(newi2c, int, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+ "\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+ "Enable or disable the radio. Use only if autodetection\n"
+ "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+ "Only use this option if your card is not detected properly.\n"
+ "\t\tSpecify card type:\n"
+ "\t\t\t 1 = WinTV PVR 250\n"
+ "\t\t\t 2 = WinTV PVR 350\n"
+ "\t\t\t 3 = WinTV PVR-150 or PVR-500\n"
+ "\t\t\t 4 = AVerMedia M179\n"
+ "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n"
+ "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n"
+ "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n"
+ "\t\t\t 8 = Adaptec AVC-2410\n"
+ "\t\t\t 9 = Adaptec AVC-2010\n"
+ "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n"
+ "\t\t\t11 = AOpen VA2000MAX-STN6\n"
+ "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n"
+ "\t\t\t13 = I/O Data GV-MVP/RX\n"
+ "\t\t\t14 = I/O Data GV-MVP/RX2E\n"
+ "\t\t\t15 = GOTVIEW PCI DVD\n"
+ "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n"
+ "\t\t\t17 = Yuan MPC622\n"
+ "\t\t\t18 = Digital Cowboy DCT-MTVP1\n"
+#ifdef HAVE_XC3028
+ "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01\n"
+#endif
+ "\t\t\t 0 = Autodetect (default)\n"
+ "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: errors only\n"
+ "\t\t\t(debug = 511 gives full debugging)");
+MODULE_PARM_DESC(ivtv_pci_latency,
+ "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+ "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(ivtv_yuv_mode,
+ "Specify the yuv playback mode:\n"
+ "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n"
+ "\t\t\tDefault: 0 (interlaced)");
+MODULE_PARM_DESC(ivtv_yuv_threshold,
+ "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n"
+ "\t\t\tDefault: 480");;
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_buffers,
+ "Encoder YUV Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_buffers,
+ "Encoder VBI Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_buffers,
+ "Encoder PCM buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(dec_mpg_buffers,
+ "Decoder MPG buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS));
+MODULE_PARM_DESC(dec_yuv_buffers,
+ "Decoder YUV buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS));
+MODULE_PARM_DESC(dec_vbi_buffers,
+ "Decoder VBI buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS));
+MODULE_PARM_DESC(newi2c,
+ "Use new I2C implementation\n"
+ "\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
+ "\t\t\tDefault is autodetect");
+
+MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card");
+
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_DESCRIPTION("CX23415/CX23416 driver");
+MODULE_SUPPORTED_DEVICE
+ ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n"
+ "\t\t\tYuan MPG series and similar)");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(IVTV_VERSION);
+
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask)
+{
+ itv->irqmask &= ~mask;
+ write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask)
+{
+ itv->irqmask |= mask;
+ write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+int ivtv_set_output_mode(struct ivtv *itv, int mode)
+{
+ int old_mode;
+
+ spin_lock(&itv->lock);
+ old_mode = itv->output_mode;
+ if (old_mode == 0)
+ itv->output_mode = old_mode = mode;
+ spin_unlock(&itv->lock);
+ return old_mode;
+}
+
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv)
+{
+ switch (itv->output_mode) {
+ case OUT_MPG:
+ return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+ case OUT_YUV:
+ return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+ default:
+ return NULL;
+ }
+}
+
+int ivtv_waitq(wait_queue_head_t *waitq)
+{
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
+ schedule();
+ finish_wait(waitq, &wait);
+ return signal_pending(current) ? -EINTR : 0;
+}
+
+/* Generic utility functions */
+int ivtv_sleep_timeout(int timeout, int intr)
+{
+ int ret;
+
+ do {
+ set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
+ if (intr && (ret = signal_pending(current)))
+ return ret;
+ } while (timeout);
+ return 0;
+}
+
+/* Release ioremapped memory */
+static void ivtv_iounmap(struct ivtv *itv)
+{
+ if (itv == NULL)
+ return;
+
+ /* Release registers memory */
+ if (itv->reg_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing reg_mem\n");
+ iounmap(itv->reg_mem);
+ itv->reg_mem = NULL;
+ }
+ /* Release io memory */
+ if (itv->has_cx23415 && itv->dec_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing dec_mem\n");
+ iounmap(itv->dec_mem);
+ }
+ itv->dec_mem = NULL;
+
+ /* Release io memory */
+ if (itv->enc_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing enc_mem\n");
+ iounmap(itv->enc_mem);
+ itv->enc_mem = NULL;
+ }
+}
+
+/* Hauppauge card? get values from tveeprom */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv)
+{
+ u8 eedata[256];
+
+ itv->i2c_client.addr = 0xA0 >> 1;
+ tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata));
+ tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata);
+}
+
+static void ivtv_process_eeprom(struct ivtv *itv)
+{
+ struct tveeprom tv;
+ int pci_slot = PCI_SLOT(itv->dev->devfn);
+
+ ivtv_read_eeprom(itv, &tv);
+
+ /* Many thanks to Steven Toth from Hauppauge for providing the
+ model numbers */
+ switch (tv.model) {
+ /* In a few cases the PCI subsystem IDs do not correctly
+ identify the card. A better method is to check the
+ model number from the eeprom instead. */
+ case 32000 ... 32999:
+ case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */
+ case 48400 ... 48599:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_250);
+ break;
+ case 48100 ... 48399:
+ case 48600 ... 48999:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_350);
+ break;
+ case 23000 ... 23999: /* PVR500 */
+ case 25000 ... 25999: /* Low profile PVR150 */
+ case 26000 ... 26999: /* Regular PVR150 */
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ break;
+ case 0:
+ IVTV_ERR("Invalid EEPROM\n");
+ return;
+ default:
+ IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model);
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ break;
+ }
+
+ switch (tv.model) {
+ /* Old style PVR350 (with an saa7114) uses this input for
+ the tuner. */
+ case 48254:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1);
+ break;
+ default:
+ break;
+ }
+
+ itv->v4l2_cap = itv->card->v4l2_capabilities;
+ itv->card_name = itv->card->name;
+
+ /* If this is a PVR500 then it should be possible to detect whether it is the
+ first or second unit by looking at the subsystem device ID: is bit 4 is
+ set, then it is the second unit (according to info from Hauppauge).
+
+ However, while this works for most cards, I have seen a few PVR500 cards
+ where both units have the same subsystem ID.
+
+ So instead I look at the reported 'PCI slot' (which is the slot on the PVR500
+ PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise
+ it is the second unit. It is possible that it is a different slot when ivtv is
+ used in Xen, in that case I ignore this card here. The worst that can happen
+ is that the card presents itself with a non-working radio device.
+
+ This detection is needed since the eeprom reports incorrectly that a radio is
+ present on the second unit. */
+ if (tv.model / 1000 == 23) {
+ itv->card_name = "WinTV PVR 500";
+ if (pci_slot == 8 || pci_slot == 9) {
+ int is_first = (pci_slot & 1) == 0;
+
+ itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" :
+ "WinTV PVR 500 (unit #2)";
+ if (!is_first) {
+ IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n");
+ tv.has_radio = 0;
+ }
+ }
+ }
+ IVTV_INFO("Autodetected %s\n", itv->card_name);
+
+ switch (tv.tuner_hauppauge_model) {
+ case 85:
+ case 99:
+ case 112:
+ itv->pvr150_workaround = 1;
+ break;
+ default:
+ break;
+ }
+ if (tv.tuner_type == TUNER_ABSENT)
+ IVTV_ERR("tveeprom cannot autodetect tuner!");
+
+ if (itv->options.tuner == -1)
+ itv->options.tuner = tv.tuner_type;
+ if (itv->options.radio == -1)
+ itv->options.radio = (tv.has_radio != 0);
+ /* only enable newi2c if an IR blaster is present */
+ /* FIXME: for 2.6.20 the test against 2 should be removed */
+ if (itv->options.newi2c == -1 && tv.has_ir != -1 && tv.has_ir != 2) {
+ itv->options.newi2c = (tv.has_ir & 2) ? 1 : 0;
+ if (itv->options.newi2c) {
+ IVTV_INFO("reopen i2c bus for IR-blaster support\n");
+ exit_ivtv_i2c(itv);
+ init_ivtv_i2c(itv);
+ }
+ }
+
+ if (itv->std != 0)
+ /* user specified tuner standard */
+ return;
+
+ /* autodetect tuner standard */
+ if (tv.tuner_formats & V4L2_STD_PAL) {
+ IVTV_DEBUG_INFO("PAL tuner detected\n");
+ itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+ IVTV_DEBUG_INFO("NTSC tuner detected\n");
+ itv->std |= V4L2_STD_NTSC_M;
+ } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+ IVTV_DEBUG_INFO("SECAM tuner detected\n");
+ itv->std |= V4L2_STD_SECAM_L;
+ } else {
+ IVTV_INFO("No tuner detected, default to NTSC-M\n");
+ itv->std |= V4L2_STD_NTSC_M;
+ }
+}
+
+static v4l2_std_id ivtv_parse_std(struct ivtv *itv)
+{
+ switch (pal[0]) {
+ case '6':
+ return V4L2_STD_PAL_60;
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ return V4L2_STD_PAL_BG;
+ case 'h':
+ case 'H':
+ return V4L2_STD_PAL_H;
+ case 'n':
+ case 'N':
+ if (pal[1] == 'c' || pal[1] == 'C')
+ return V4L2_STD_PAL_Nc;
+ return V4L2_STD_PAL_N;
+ case 'i':
+ case 'I':
+ return V4L2_STD_PAL_I;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_PAL_DK;
+ case 'M':
+ case 'm':
+ return V4L2_STD_PAL_M;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("pal= argument not recognised\n");
+ return 0;
+ }
+
+ switch (secam[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_SECAM_DK;
+ case 'l':
+ case 'L':
+ if (secam[1] == 'C' || secam[1] == 'c')
+ return V4L2_STD_SECAM_LC;
+ return V4L2_STD_SECAM_L;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("secam= argument not recognised\n");
+ return 0;
+ }
+
+ switch (ntsc[0]) {
+ case 'm':
+ case 'M':
+ return V4L2_STD_NTSC_M;
+ case 'j':
+ case 'J':
+ return V4L2_STD_NTSC_M_JP;
+ case 'k':
+ case 'K':
+ return V4L2_STD_NTSC_M_KR;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("ntsc= argument not recognised\n");
+ return 0;
+ }
+
+ /* no match found */
+ return 0;
+}
+
+static void ivtv_process_options(struct ivtv *itv)
+{
+ const char *chipname;
+ int i, j;
+
+ itv->options.megabytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+ itv->options.megabytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+ itv->options.megabytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+ itv->options.megabytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ itv->options.megabytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers;
+ itv->options.megabytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers;
+ itv->options.megabytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
+ itv->options.cardtype = cardtype[itv->num];
+ itv->options.tuner = tuner[itv->num];
+ itv->options.radio = radio[itv->num];
+ itv->options.newi2c = newi2c;
+
+ itv->std = ivtv_parse_std(itv);
+ itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15);
+ chipname = itv->has_cx23415 ? "cx23415" : "cx23416";
+ if (itv->options.cardtype == -1) {
+ IVTV_INFO("Ignore card (detected %s based chip)\n", chipname);
+ return;
+ }
+ if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) {
+ IVTV_INFO("User specified %s card (detected %s based chip)\n",
+ itv->card->name, chipname);
+ } else if (itv->options.cardtype != 0) {
+ IVTV_ERR("Unknown user specified type, trying to autodetect card\n");
+ }
+ if (itv->card == NULL) {
+ if (itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
+ itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
+ itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
+ itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150);
+ IVTV_INFO("Autodetected Hauppauge card (%s based)\n",
+ chipname);
+ }
+ }
+ if (itv->card == NULL) {
+ for (i = 0; (itv->card = ivtv_get_card(i)); i++) {
+ if (itv->card->pci_list == NULL)
+ continue;
+ for (j = 0; itv->card->pci_list[j].device; j++) {
+ if (itv->dev->device !=
+ itv->card->pci_list[j].device)
+ continue;
+ if (itv->dev->subsystem_vendor !=
+ itv->card->pci_list[j].subsystem_vendor)
+ continue;
+ if (itv->dev->subsystem_device !=
+ itv->card->pci_list[j].subsystem_device)
+ continue;
+ IVTV_INFO("Autodetected %s card (%s based)\n",
+ itv->card->name, chipname);
+ goto done;
+ }
+ }
+ }
+done:
+
+ if (itv->card == NULL) {
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n",
+ itv->dev->vendor, itv->dev->device);
+ IVTV_ERR(" subsystem vendor/device: %04x/%04x\n",
+ itv->dev->subsystem_vendor, itv->dev->subsystem_device);
+ IVTV_ERR(" %s based\n", chipname);
+ IVTV_ERR("Defaulting to %s card\n", itv->card->name);
+ IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+ IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+ IVTV_ERR("Prefix your subject line with [UNKNOWN CARD].\n");
+ }
+ itv->v4l2_cap = itv->card->v4l2_capabilities;
+ itv->card_name = itv->card->name;
+}
+
+/* Precondition: the ivtv structure has been memset to 0. Only
+ the dev and num fields have been filled in.
+ No assumptions on the card type may be made here (see ivtv_init_struct2
+ for that).
+ */
+static int __devinit ivtv_init_struct1(struct ivtv *itv)
+{
+ itv->base_addr = pci_resource_start(itv->dev, 0);
+ itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */
+ itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */
+
+ mutex_init(&itv->i2c_bus_lock);
+ mutex_init(&itv->udma.lock);
+
+ spin_lock_init(&itv->lock);
+ spin_lock_init(&itv->dma_reg_lock);
+
+ itv->irq_work_queues = create_workqueue(itv->name);
+ if (itv->irq_work_queues == NULL) {
+ IVTV_ERR("Could not create ivtv workqueue\n");
+ return -1;
+ }
+
+ INIT_WORK(&itv->irq_work_queue, ivtv_irq_work_handler);
+
+ /* start counting open_id at 1 */
+ itv->open_id = 1;
+
+ /* Initial settings */
+ cx2341x_fill_defaults(&itv->params);
+ itv->params.port = CX2341X_PORT_MEMORY;
+ itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+ init_waitqueue_head(&itv->cap_w);
+ init_waitqueue_head(&itv->event_waitq);
+ init_waitqueue_head(&itv->vsync_waitq);
+ init_waitqueue_head(&itv->dma_waitq);
+ init_timer(&itv->dma_timer);
+ itv->dma_timer.function = ivtv_unfinished_dma;
+ itv->dma_timer.data = (unsigned long)itv;
+
+ itv->cur_dma_stream = -1;
+ itv->audio_stereo_mode = AUDIO_STEREO;
+ itv->audio_bilingual_mode = AUDIO_MONO_LEFT;
+
+ /* Ctrls */
+ itv->speed = 1000;
+
+ /* VBI */
+ itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced;
+
+ /* OSD */
+ itv->osd_global_alpha_state = 1;
+ itv->osd_global_alpha = 255;
+
+ /* YUV */
+ atomic_set(&itv->yuv_info.next_dma_frame, -1);
+ itv->yuv_info.lace_mode = ivtv_yuv_mode;
+ itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
+ return 0;
+}
+
+/* Second initialization part. Here the card type has been
+ autodetected. */
+static void __devinit ivtv_init_struct2(struct ivtv *itv)
+{
+ int i;
+
+ for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
+ if (itv->card->video_inputs[i].video_type == 0)
+ break;
+ itv->nof_inputs = i;
+ for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
+ if (itv->card->audio_inputs[i].audio_type == 0)
+ break;
+ itv->nof_audio_inputs = i;
+
+ /* 0x00EF = saa7114(239) 0x00F0 = saa7115(240) 0x0106 = micro */
+ if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X))
+ itv->digitizer = 0xF1;
+ else if (itv->card->hw_all & IVTV_HW_SAA7114)
+ itv->digitizer = 0xEF;
+ else /* cx25840 */
+ itv->digitizer = 0x140;
+
+ if (itv->card->hw_all & IVTV_HW_CX25840) {
+ itv->vbi.sliced_size = 288; /* multiple of 16, real size = 284 */
+ } else {
+ itv->vbi.sliced_size = 64; /* multiple of 16, real size = 52 */
+ }
+
+ /* Find tuner input */
+ for (i = 0; i < itv->nof_inputs; i++) {
+ if (itv->card->video_inputs[i].video_type ==
+ IVTV_CARD_INPUT_VID_TUNER)
+ break;
+ }
+ if (i == itv->nof_inputs)
+ i = 0;
+ itv->active_input = i;
+ itv->audio_input = itv->card->video_inputs[i].audio_index;
+ if (itv->card->hw_all & IVTV_HW_CX25840)
+ itv->video_dec_func = ivtv_cx25840;
+ else if (itv->card->hw_all & IVTV_HW_SAA717X)
+ itv->video_dec_func = ivtv_saa717x;
+ else
+ itv->video_dec_func = ivtv_saa7115;
+}
+
+static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ u16 cmd;
+ unsigned char pci_latency;
+
+ IVTV_DEBUG_INFO("Enabling pci device\n");
+
+ if (pci_enable_device(dev)) {
+ IVTV_ERR("Can't enable device %d!\n", itv->num);
+ return -EIO;
+ }
+ if (pci_set_dma_mask(dev, 0xffffffff)) {
+ IVTV_ERR("No suitable DMA available on card %d.\n", itv->num);
+ return -EIO;
+ }
+ if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
+ IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num);
+ return -EIO;
+ }
+
+ if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
+ IVTV_REG_SIZE, "ivtv registers")) {
+ IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num);
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ return -EIO;
+ }
+
+ if (itv->has_cx23415 &&
+ !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
+ IVTV_DECODER_SIZE, "ivtv decoder")) {
+ IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num);
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ return -EIO;
+ }
+
+ /* Check for bus mastering */
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n");
+ pci_set_master(dev);
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ IVTV_ERR("Bus Mastering is not enabled\n");
+ return -ENXIO;
+ }
+ }
+ IVTV_DEBUG_INFO("Bus Mastering Enabled.\n");
+
+ pci_read_config_byte(dev, PCI_CLASS_REVISION, &itv->card_rev);
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 64 && ivtv_pci_latency) {
+ IVTV_INFO("Unreasonably low latency timer, "
+ "setting to 64 (was %d)\n", pci_latency);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+ }
+ /* This config space value relates to DMA latencies. The
+ default value 0x8080 is too low however and will lead
+ to DMA errors. 0xffff is the max value which solves
+ these problems. */
+ pci_write_config_dword(dev, 0x40, 0xffff);
+
+ IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, "
+ "irq: %d, latency: %d, memory: 0x%lx\n",
+ itv->dev->device, itv->card_rev, dev->bus->number,
+ PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+ itv->dev->irq, pci_latency, (unsigned long)itv->base_addr);
+
+ return 0;
+}
+
+static void ivtv_request_module(struct ivtv *itv, const char *name)
+{
+ if (request_module(name) != 0) {
+ IVTV_ERR("Failed to load module %s\n", name);
+ } else {
+ IVTV_DEBUG_INFO("Loaded module %s\n", name);
+ }
+}
+
+static void ivtv_load_and_init_modules(struct ivtv *itv)
+{
+ struct v4l2_control ctrl;
+ u32 hw = itv->card->hw_all;
+ int i;
+
+ /* load modules */
+#ifndef CONFIG_VIDEO_TUNER
+ if (hw & IVTV_HW_TUNER) {
+ ivtv_request_module(itv, "tuner");
+#ifdef HAVE_XC3028
+ if (itv->options.tuner == TUNER_XCEIVE_XC3028)
+ ivtv_request_module(itv, "xc3028-tuner");
+#endif
+ }
+#endif
+#ifndef CONFIG_VIDEO_CX25840
+ if (hw & IVTV_HW_CX25840)
+ ivtv_request_module(itv, "cx25840");
+#endif
+#ifndef CONFIG_VIDEO_SAA711X
+ if (hw & IVTV_HW_SAA711X)
+ ivtv_request_module(itv, "saa7115");
+#endif
+#ifndef CONFIG_VIDEO_SAA7127
+ if (hw & IVTV_HW_SAA7127)
+ ivtv_request_module(itv, "saa7127");
+#endif
+ if (hw & IVTV_HW_SAA717X)
+ ivtv_request_module(itv, "saa717x");
+#ifndef CONFIG_VIDEO_UPD64031A
+ if (hw & IVTV_HW_UPD64031A)
+ ivtv_request_module(itv, "upd64031a");
+#endif
+#ifndef CONFIG_VIDEO_UPD64083
+ if (hw & IVTV_HW_UPD6408X)
+ ivtv_request_module(itv, "upd64083");
+#endif
+#ifndef CONFIG_VIDEO_MSP3400
+ if (hw & IVTV_HW_MSP34XX)
+ ivtv_request_module(itv, "msp3400");
+#endif
+ if (hw & IVTV_HW_TVAUDIO)
+ ivtv_request_module(itv, "tvaudio");
+#ifndef CONFIG_VIDEO_WM8775
+ if (hw & IVTV_HW_WM8775)
+ ivtv_request_module(itv, "wm8775");
+#endif
+#ifndef CONFIG_VIDEO_WM8739
+ if (hw & IVTV_HW_WM8739)
+ ivtv_request_module(itv, "wm8739");
+#endif
+#ifndef CONFIG_VIDEO_CS53L32A
+ if (hw & IVTV_HW_CS53L32A)
+ ivtv_request_module(itv, "cs53l32a");
+#endif
+
+ /* check which i2c devices are actually found */
+ for (i = 0; i < 32; i++) {
+ u32 device = 1 << i;
+
+ if (!(device & hw))
+ continue;
+ if (device == IVTV_HW_GPIO) {
+ /* GPIO is always available */
+ itv->hw_flags |= IVTV_HW_GPIO;
+ continue;
+ }
+ if (ivtv_i2c_hw_addr(itv, device) > 0)
+ itv->hw_flags |= device;
+ }
+
+ hw = itv->hw_flags;
+
+ if (itv->card->type == IVTV_CARD_CX23416GYC) {
+ /* Several variations of this card exist, detect which card
+ type should be used. */
+ if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0)
+ itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS);
+ else if ((hw & IVTV_HW_UPD64031A) == 0)
+ itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR);
+ }
+
+ if (hw & IVTV_HW_CX25840) {
+ /* CX25840_CID_ENABLE_PVR150_WORKAROUND */
+ ctrl.id = V4L2_CID_PRIVATE_BASE;
+ ctrl.value = itv->pvr150_workaround;
+ itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl);
+
+ itv->vbi.raw_decoder_line_size = 1444;
+ itv->vbi.raw_decoder_sav_odd_field = 0x20;
+ itv->vbi.raw_decoder_sav_even_field = 0x60;
+ itv->vbi.sliced_decoder_line_size = 272;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xB0;
+ itv->vbi.sliced_decoder_sav_even_field = 0xF0;
+ }
+
+ if (hw & IVTV_HW_SAA711X) {
+ struct v4l2_chip_ident v = { V4L2_CHIP_MATCH_I2C_DRIVER, I2C_DRIVERID_SAA711X };
+
+ /* determine the exact saa711x model */
+ itv->hw_flags &= ~IVTV_HW_SAA711X;
+
+ ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v);
+ if (v.ident == V4L2_IDENT_SAA7114) {
+ itv->hw_flags |= IVTV_HW_SAA7114;
+ /* VBI is not yet supported by the saa7114 driver. */
+ itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE);
+ }
+ else {
+ itv->hw_flags |= IVTV_HW_SAA7115;
+ }
+ itv->vbi.raw_decoder_line_size = 1443;
+ itv->vbi.raw_decoder_sav_odd_field = 0x25;
+ itv->vbi.raw_decoder_sav_even_field = 0x62;
+ itv->vbi.sliced_decoder_line_size = 51;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+ itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+ }
+
+ if (hw & IVTV_HW_SAA717X) {
+ itv->vbi.raw_decoder_line_size = 1443;
+ itv->vbi.raw_decoder_sav_odd_field = 0x25;
+ itv->vbi.raw_decoder_sav_even_field = 0x62;
+ itv->vbi.sliced_decoder_line_size = 51;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+ itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+ }
+}
+
+static int __devinit ivtv_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ int retval = 0;
+ int video_input;
+ int yuv_buf_size;
+ int vbi_buf_size;
+ int fw_retry_count = 3;
+ struct ivtv *itv;
+ struct v4l2_frequency vf;
+
+ spin_lock(&ivtv_cards_lock);
+
+ /* Make sure we've got a place for this card */
+ if (ivtv_cards_active == IVTV_MAX_CARDS) {
+ printk(KERN_ERR "ivtv: Maximum number of cards detected (%d).\n",
+ ivtv_cards_active);
+ spin_unlock(&ivtv_cards_lock);
+ return -ENOMEM;
+ }
+
+ itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
+ if (itv == 0) {
+ spin_unlock(&ivtv_cards_lock);
+ return -ENOMEM;
+ }
+ ivtv_cards[ivtv_cards_active] = itv;
+ itv->dev = dev;
+ itv->num = ivtv_cards_active++;
+ snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num);
+ if (itv->num) {
+ printk(KERN_INFO "ivtv: ====================== NEXT CARD ======================\n");
+ }
+
+ spin_unlock(&ivtv_cards_lock);
+
+ ivtv_process_options(itv);
+ if (itv->options.cardtype == -1) {
+ retval = -ENODEV;
+ goto err;
+ }
+ if (ivtv_init_struct1(itv)) {
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
+
+ /* PCI Device Setup */
+ if ((retval = ivtv_setup_pci(itv, dev, pci_id)) != 0) {
+ if (retval == -EIO)
+ goto free_workqueue;
+ else if (retval == -ENXIO)
+ goto free_mem;
+ }
+ /* save itv in the pci struct for later use */
+ pci_set_drvdata(dev, itv);
+
+ /* map io memory */
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE);
+ itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET,
+ IVTV_ENCODER_SIZE);
+ if (!itv->enc_mem) {
+ IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+ IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+
+ if (itv->has_cx23415) {
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+ itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET,
+ IVTV_DECODER_SIZE);
+ if (!itv->dec_mem) {
+ IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+ IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+ }
+ else {
+ itv->dec_mem = itv->enc_mem;
+ }
+
+ /* map registers memory */
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ itv->reg_mem =
+ ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (!itv->reg_mem) {
+ IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+ IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+ retval = -ENOMEM;
+ goto free_io;
+ }
+
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (ivtv_firmware_init(itv) == 0)
+ break;
+ if (fw_retry_count > 1)
+ IVTV_WARN("Retry loading firmware\n");
+ }
+ if (fw_retry_count == 0) {
+ IVTV_ERR("Error initializing firmware\n");
+ goto free_i2c;
+ }
+
+ /* Try and get firmware versions */
+ IVTV_DEBUG_INFO("Getting firmware version..\n");
+ ivtv_firmware_versions(itv);
+
+ /* Check yuv output filter table */
+ if (itv->has_cx23415) ivtv_yuv_filter_check(itv);
+
+ ivtv_gpio_init(itv);
+
+ /* active i2c */
+ IVTV_DEBUG_INFO("activating i2c...\n");
+ if (init_ivtv_i2c(itv)) {
+ IVTV_ERR("Could not initialize i2c\n");
+ goto free_irq;
+ }
+
+ IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active);
+
+ if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
+#ifdef CONFIG_VIDEO_TVEEPROM_MODULE
+ ivtv_request_module(itv, "tveeprom");
+#endif
+ /* Based on the model number the cardtype may be changed.
+ The PCI IDs are not always reliable. */
+ ivtv_process_eeprom(itv);
+ }
+
+ if (itv->std == 0) {
+ itv->std = V4L2_STD_NTSC_M;
+ }
+
+ if (itv->options.tuner == -1) {
+ int i;
+
+ for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) {
+ if ((itv->std & itv->card->tuners[i].std) == 0)
+ continue;
+ itv->options.tuner = itv->card->tuners[i].tuner;
+ break;
+ }
+ }
+ /* if no tuner was found, then pick the first tuner in the card list */
+ if (itv->options.tuner == -1 && itv->card->tuners[0].std) {
+ itv->std = itv->card->tuners[0].std;
+ itv->options.tuner = itv->card->tuners[0].tuner;
+ }
+ if (itv->options.radio == -1)
+ itv->options.radio = (itv->card->radio_input.audio_type != 0);
+
+ /* The card is now fully identified, continue with card-specific
+ initialization. */
+ ivtv_init_struct2(itv);
+
+ ivtv_load_and_init_modules(itv);
+
+ if (itv->std & V4L2_STD_525_60) {
+ itv->is_60hz = 1;
+ itv->is_out_60hz = 1;
+ } else {
+ itv->is_50hz = 1;
+ itv->is_out_50hz = 1;
+ }
+ itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
+
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000;
+
+ /* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */
+ yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8;
+
+ /* Setup VBI Raw Size. Should be big enough to hold PAL.
+ It is possible to switch between PAL and NTSC, so we need to
+ take the largest size here. */
+ /* 1456 is multiple of 16, real size = 1444 */
+ itv->vbi.raw_size = 1456;
+ /* We use a buffer size of 1/2 of the total size needed for a
+ frame. This is actually very useful, since we now receive
+ a field at a time and that makes 'compressing' the raw data
+ down to size by stripping off the SAV codes a lot easier.
+ Note: having two different buffer sizes prevents standard
+ switching on the fly. We need to find a better solution... */
+ vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+ if (itv->options.radio > 0)
+ itv->v4l2_cap |= V4L2_CAP_RADIO;
+
+ if (itv->options.tuner > -1) {
+ struct tuner_setup setup;
+
+ setup.addr = ADDR_UNSET;
+ setup.type = itv->options.tuner;
+ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+#ifdef HAVE_XC3028
+ setup.initmode = V4L2_TUNER_ANALOG_TV;
+ if (itv->options.tuner == TUNER_XCEIVE_XC3028) {
+ setup.gpio_write = ivtv_reset_tuner_gpio;
+ setup.gpio_priv = itv;
+ }
+#endif
+ ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup);
+ }
+
+ vf.tuner = 0;
+ vf.type = V4L2_TUNER_ANALOG_TV;
+ vf.frequency = 6400; /* the tuner 'baseline' frequency */
+ if (itv->std & V4L2_STD_NTSC_M) {
+ /* Why on earth? */
+ vf.frequency = 1076; /* ch. 4 67250*16/1000 */
+ }
+
+ /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+ are not. */
+ itv->tuner_std = itv->std;
+
+ video_input = itv->active_input;
+ itv->active_input++; /* Force update of input */
+ ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input);
+
+ /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+ in one place. */
+ itv->std++; /* Force full standard initialization */
+ itv->std_out = itv->std;
+ ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf);
+
+ retval = ivtv_streams_setup(itv);
+ if (retval) {
+ IVTV_ERR("Error %d setting up streams\n", retval);
+ goto free_i2c;
+ }
+
+ if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
+ ivtv_init_mpeg_decoder(itv);
+ }
+ ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std);
+
+ IVTV_DEBUG_IRQ("Masking interrupts\n");
+ /* clear interrupt mask, effectively disabling interrupts */
+ ivtv_set_irq_mask(itv, 0xffffffff);
+
+ /* Register IRQ */
+ retval = request_irq(itv->dev->irq, ivtv_irq_handler,
+ IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv);
+ if (retval) {
+ IVTV_ERR("Failed to register irq %d\n", retval);
+ goto free_streams;
+ }
+
+ /* On a cx23416 this seems to be able to enable DMA to the chip? */
+ if (!itv->has_cx23415)
+ write_reg_sync(0x03, IVTV_REG_DMACONTROL);
+
+ /* Default interrupts enabled. For the PVR350 this includes the
+ decoder VSYNC interrupt, which is always on. It is not only used
+ during decoding but also by the OSD.
+ Some old PVR250 cards had a cx23415, so testing for that is too
+ general. Instead test if the card has video output capability. */
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC);
+ else
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT);
+
+ if (itv->has_cx23415)
+ ivtv_set_osd_alpha(itv);
+
+ IVTV_INFO("Initialized %s, card #%d\n", itv->card_name, itv->num);
+
+ return 0;
+
+ free_irq:
+ free_irq(itv->dev->irq, (void *)itv);
+ free_streams:
+ ivtv_streams_cleanup(itv);
+ free_i2c:
+ exit_ivtv_i2c(itv);
+ free_io:
+ ivtv_iounmap(itv);
+ free_mem:
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (itv->has_cx23415)
+ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+ free_workqueue:
+ destroy_workqueue(itv->irq_work_queues);
+ err:
+ if (retval == 0)
+ retval = -ENODEV;
+ IVTV_ERR("Error %d on initialization\n", retval);
+
+ kfree(ivtv_cards[ivtv_cards_active]);
+ ivtv_cards[ivtv_cards_active] = NULL;
+ return retval;
+}
+
+static void ivtv_remove(struct pci_dev *pci_dev)
+{
+ struct ivtv *itv = pci_get_drvdata(pci_dev);
+
+ IVTV_DEBUG_INFO("Removing Card #%d.\n", itv->num);
+
+ /* Stop all captures */
+ IVTV_DEBUG_INFO(" Stopping all streams.\n");
+ if (atomic_read(&itv->capturing) > 0)
+ ivtv_stop_all_captures(itv);
+
+ /* Stop all decoding */
+ IVTV_DEBUG_INFO(" Stopping decoding.\n");
+ if (atomic_read(&itv->decoding) > 0) {
+ int type;
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ type = IVTV_DEC_STREAM_TYPE_YUV;
+ else
+ type = IVTV_DEC_STREAM_TYPE_MPG;
+ ivtv_stop_v4l2_decode_stream(&itv->streams[type],
+ VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
+ }
+
+ /* Interrupts */
+ IVTV_DEBUG_INFO(" Disabling interrupts.\n");
+ ivtv_set_irq_mask(itv, 0xffffffff);
+ del_timer_sync(&itv->dma_timer);
+
+ /* Stop all Work Queues */
+ IVTV_DEBUG_INFO(" Stop Work Queues.\n");
+ flush_workqueue(itv->irq_work_queues);
+ destroy_workqueue(itv->irq_work_queues);
+
+ IVTV_DEBUG_INFO(" Stopping Firmware.\n");
+ ivtv_halt_firmware(itv);
+
+ IVTV_DEBUG_INFO(" Unregistering v4l devices.\n");
+ ivtv_streams_cleanup(itv);
+ IVTV_DEBUG_INFO(" Freeing dma resources.\n");
+ ivtv_udma_free(itv);
+
+ exit_ivtv_i2c(itv);
+
+ IVTV_DEBUG_INFO(" Releasing irq.\n");
+ free_irq(itv->dev->irq, (void *)itv);
+
+ if (itv->dev) {
+ ivtv_iounmap(itv);
+ }
+
+ IVTV_DEBUG_INFO(" Releasing mem.\n");
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (itv->has_cx23415)
+ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+
+ pci_disable_device(itv->dev);
+
+ IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num);
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver ivtv_pci_driver = {
+ .name = "ivtv",
+ .id_table = ivtv_pci_tbl,
+ .probe = ivtv_probe,
+ .remove = ivtv_remove,
+};
+
+static int module_start(void)
+{
+ printk(KERN_INFO "ivtv: ==================== START INIT IVTV ====================\n");
+ printk(KERN_INFO "ivtv: version %s (" VERMAGIC_STRING ") loading\n", IVTV_VERSION);
+
+ memset(ivtv_cards, 0, sizeof(ivtv_cards));
+
+ /* Validate parameters */
+ if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
+ printk(KERN_ERR "ivtv: ivtv_first_minor must be between 0 and %d. Exiting...\n",
+ IVTV_MAX_CARDS - 1);
+ return -1;
+ }
+
+ if (ivtv_debug < 0 || ivtv_debug > 511) {
+ ivtv_debug = 0;
+ printk(KERN_INFO "ivtv: debug value must be >= 0 and <= 511!\n");
+ }
+
+ if (pci_register_driver(&ivtv_pci_driver)) {
+ printk(KERN_ERR "ivtv: Error detecting PCI card\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "ivtv: ==================== END INIT IVTV ====================\n");
+ return 0;
+}
+
+static void module_cleanup(void)
+{
+ int i, j;
+
+ pci_unregister_driver(&ivtv_pci_driver);
+
+ for (i = 0; i < ivtv_cards_active; i++) {
+ if (ivtv_cards[i] == NULL)
+ continue;
+ for (j = 0; j < IVTV_VBI_FRAMES; j++) {
+ kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]);
+ }
+ kfree(ivtv_cards[i]);
+ }
+}
+
+/* Note: These symbols are exported because they are used by the ivtv-fb
+ framebuffer module and an infrared module for the IR-blaster. */
+EXPORT_SYMBOL(ivtv_set_irq_mask);
+EXPORT_SYMBOL(ivtv_cards_active);
+EXPORT_SYMBOL(ivtv_cards);
+EXPORT_SYMBOL(ivtv_api);
+EXPORT_SYMBOL(ivtv_vapi);
+EXPORT_SYMBOL(ivtv_vapi_result);
+EXPORT_SYMBOL(ivtv_clear_irq_mask);
+EXPORT_SYMBOL(ivtv_debug);
+EXPORT_SYMBOL(ivtv_reset_ir_gpio);
+EXPORT_SYMBOL(ivtv_udma_setup);
+EXPORT_SYMBOL(ivtv_udma_unmap);
+EXPORT_SYMBOL(ivtv_udma_alloc);
+EXPORT_SYMBOL(ivtv_udma_prepare);
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
new file mode 100644
index 000000000000..9a412d6c6d06
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -0,0 +1,868 @@
+/*
+ ivtv driver internal defines and structures
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+#ifndef IVTV_DRIVER_H
+#define IVTV_DRIVER_H
+
+/* Internal header for ivtv project:
+ * Driver for the cx23415/6 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/byteorder/swab.h>
+#include <linux/pagemap.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <media/cx2341x.h>
+
+/* #define HAVE_XC3028 1 */
+
+#include <media/ivtv.h>
+
+#ifdef CONFIG_LIRC_I2C
+# error "This driver is not compatible with the LIRC I2C kernel configuration option."
+#endif /* CONFIG_LIRC_I2C */
+
+#ifndef CONFIG_PCI
+# error "This driver requires kernel PCI support."
+#endif /* CONFIG_PCI */
+
+#define IVTV_ENCODER_OFFSET 0x00000000
+#define IVTV_ENCODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */
+
+#define IVTV_DECODER_OFFSET 0x01000000
+#define IVTV_DECODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */
+
+#define IVTV_REG_OFFSET 0x02000000
+#define IVTV_REG_SIZE 0x00010000
+
+/* Buffers on hardware offsets */
+#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */
+#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */
+
+/* Offset to filter table in firmware */
+#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8
+#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358
+
+extern const u32 yuv_offset[4];
+
+/* Maximum ivtv driver instances.
+ Based on 6 PVR500s each with two PVR15s...
+ TODO: make this dynamic. I believe it is only a global in order to support
+ ivtv-fb. There must be a better way to do that. */
+#define IVTV_MAX_CARDS 12
+
+/* Supported cards */
+#define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */
+#define IVTV_CARD_PVR_350 1 /* encoder, decoder, tv-out */
+#define IVTV_CARD_PVR_150 2 /* WinTV PVR 150 and PVR 500 (really just two
+ PVR150s on one PCI board) */
+#define IVTV_CARD_M179 3 /* AVerMedia M179 (encoder only) */
+#define IVTV_CARD_MPG600 4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */
+#define IVTV_CARD_MPG160 5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160
+ cx23415 based, but does not have tv-out */
+#define IVTV_CARD_PG600 6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */
+#define IVTV_CARD_AVC2410 7 /* Adaptec AVC-2410 */
+#define IVTV_CARD_AVC2010 8 /* Adaptec AVD-2010 (No Tuner) */
+#define IVTV_CARD_TG5000TV 9 /* NAGASE TRANSGEAR 5000TV, encoder only */
+#define IVTV_CARD_VA2000MAX_SNT6 10 /* VA2000MAX-STN6 */
+#define IVTV_CARD_CX23416GYC 11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_GV_MVPRX 12 /* I/O Data GV-MVP/RX, RX2, RX2W */
+#define IVTV_CARD_GV_MVPRX2E 13 /* I/O Data GV-MVP/RX2E */
+#define IVTV_CARD_GOTVIEW_PCI_DVD 14 /* GotView PCI DVD */
+#define IVTV_CARD_GOTVIEW_PCI_DVD2 15 /* GotView PCI DVD2 */
+#define IVTV_CARD_YUAN_MPC622 16 /* Yuan MPC622 miniPCI */
+#define IVTV_CARD_DCTMTVP1 17 /* DIGITAL COWBOY DCT-MTVP1 */
+#ifdef HAVE_XC3028
+#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 */
+#define IVTV_CARD_LAST 18
+#else
+#define IVTV_CARD_LAST 17
+#endif
+
+/* Variants of existing cards but with the same PCI IDs. The driver
+ detects these based on other device information.
+ These cards must always come last.
+ New cards must be inserted above, and the indices of the cards below
+ must be adjusted accordingly. */
+
+/* PVR-350 V1 (uses saa7114) */
+#define IVTV_CARD_PVR_350_V1 (IVTV_CARD_LAST+1)
+/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_CX23416GYC_NOGR (IVTV_CARD_LAST+2)
+#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3)
+
+#define IVTV_ENC_STREAM_TYPE_MPG 0
+#define IVTV_ENC_STREAM_TYPE_YUV 1
+#define IVTV_ENC_STREAM_TYPE_VBI 2
+#define IVTV_ENC_STREAM_TYPE_PCM 3
+#define IVTV_ENC_STREAM_TYPE_RAD 4
+#define IVTV_DEC_STREAM_TYPE_MPG 5
+#define IVTV_DEC_STREAM_TYPE_VBI 6
+#define IVTV_DEC_STREAM_TYPE_VOUT 7
+#define IVTV_DEC_STREAM_TYPE_YUV 8
+#define IVTV_MAX_STREAMS 9
+
+#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */
+#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */
+#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */
+#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */
+
+#define IVTV_ENC_MEM_START 0x00000000
+#define IVTV_DEC_MEM_START 0x01000000
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_ICOMP 0x4444
+#define PCI_DEVICE_ID_IVTV15 0x0803
+#define PCI_DEVICE_ID_IVTV16 0x0016
+
+/* subsystem vendor ID */
+#define IVTV_PCI_ID_HAUPPAUGE 0x0070
+#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270
+#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070
+#define IVTV_PCI_ID_ADAPTEC 0x9005
+#define IVTV_PCI_ID_AVERMEDIA 0x1461
+#define IVTV_PCI_ID_YUAN1 0x12ab
+#define IVTV_PCI_ID_YUAN2 0xff01
+#define IVTV_PCI_ID_YUAN3 0xffab
+#define IVTV_PCI_ID_YUAN4 0xfbab
+#define IVTV_PCI_ID_DIAMONDMM 0xff92
+#define IVTV_PCI_ID_IODATA 0x10fc
+#define IVTV_PCI_ID_MELCO 0x1154
+#define IVTV_PCI_ID_GOTVIEW1 0xffac
+#define IVTV_PCI_ID_GOTVIEW2 0xffad
+
+/* Decoder Buffer hardware size on Chip */
+#define IVTV_DEC_MAX_BUF 0x00100000 /* max bytes in decoder buffer */
+#define IVTV_DEC_MIN_BUF 0x00010000 /* min bytes in dec buffer */
+
+/* ======================================================================== */
+/* ========================== START USER SETTABLE DMA VARIABLES =========== */
+/* ======================================================================== */
+
+#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */
+
+/* DMA Buffers, Default size in MB allocated */
+#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4
+#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2
+#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1
+#define IVTV_DEFAULT_ENC_PCM_BUFFERS 1
+#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1
+#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1
+#define IVTV_DEFAULT_DEC_VBI_BUFFERS 1
+
+/* ======================================================================== */
+/* ========================== END USER SETTABLE DMA VARIABLES ============= */
+/* ======================================================================== */
+
+/* Decoder Status Register */
+#define IVTV_DMA_ERR_LIST 0x00000010
+#define IVTV_DMA_ERR_WRITE 0x00000008
+#define IVTV_DMA_ERR_READ 0x00000004
+#define IVTV_DMA_SUCCESS_WRITE 0x00000002
+#define IVTV_DMA_SUCCESS_READ 0x00000001
+#define IVTV_DMA_READ_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ)
+#define IVTV_DMA_WRITE_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE)
+#define IVTV_DMA_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ)
+
+/* DMA Registers */
+#define IVTV_REG_DMAXFER (0x0000)
+#define IVTV_REG_DMASTATUS (0x0004)
+#define IVTV_REG_DECDMAADDR (0x0008)
+#define IVTV_REG_ENCDMAADDR (0x000c)
+#define IVTV_REG_DMACONTROL (0x0010)
+#define IVTV_REG_IRQSTATUS (0x0040)
+#define IVTV_REG_IRQMASK (0x0048)
+
+/* Setup Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8)
+#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC)
+#define IVTV_REG_VDM (0x2800)
+#define IVTV_REG_AO (0x2D00)
+#define IVTV_REG_BYTEFLUSH (0x2D24)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+#define IVTV_IRQ_ENC_START_CAP (0x1 << 31)
+#define IVTV_IRQ_ENC_EOS (0x1 << 30)
+#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29)
+#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28)
+#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27)
+#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24)
+#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22)
+#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20)
+#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19)
+#define IVTV_IRQ_DMA_ERR (0x1 << 18)
+#define IVTV_IRQ_DMA_WRITE (0x1 << 17)
+#define IVTV_IRQ_DMA_READ (0x1 << 16)
+#define IVTV_IRQ_DEC_VSYNC (0x1 << 10)
+
+/* IRQ Masks */
+#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|IVTV_IRQ_DMA_READ)
+
+#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS)
+#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG)
+
+/* i2c stuff */
+#define I2C_CLIENTS_MAX 16
+
+/* debugging */
+
+#define IVTV_DBGFLG_WARN (1 << 0)
+#define IVTV_DBGFLG_INFO (1 << 1)
+#define IVTV_DBGFLG_API (1 << 2)
+#define IVTV_DBGFLG_DMA (1 << 3)
+#define IVTV_DBGFLG_IOCTL (1 << 4)
+#define IVTV_DBGFLG_I2C (1 << 5)
+#define IVTV_DBGFLG_IRQ (1 << 6)
+#define IVTV_DBGFLG_DEC (1 << 7)
+#define IVTV_DBGFLG_YUV (1 << 8)
+
+/* NOTE: extra space before comma in 'itv->num , ## args' is required for
+ gcc-2.95, otherwise it won't compile. */
+#define IVTV_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & ivtv_debug) \
+ printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+ } while (0)
+#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args)
+#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info",fmt , ## args)
+#define IVTV_DEBUG_API(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args)
+#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+#define IVTV_FB_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & ivtv_debug) \
+ printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \
+ } while (0)
+#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args)
+#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+#define IVTV_FB_DEBUG_API(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args)
+#define IVTV_FB_DEBUG_DMA(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_FB_DEBUG_I2C(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_FB_DEBUG_IRQ(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_FB_DEBUG_DEC(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_FB_DEBUG_YUV(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+/* Standard kernel messages */
+#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv%d-fb: " fmt, itv->num , ## args)
+#define IVTV_FB_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d-fb: " fmt, itv->num , ## args)
+#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args)
+
+/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
+#define MPEG_FRAME_TYPE_IFRAME 1
+#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
+#define MPEG_FRAME_TYPE_ALL 7
+
+/* output modes (cx23415 only) */
+#define OUT_NONE 0
+#define OUT_MPG 1
+#define OUT_YUV 2
+#define OUT_UDMA_YUV 3
+#define OUT_PASSTHROUGH 4
+
+#define IVTV_MAX_PGM_INDEX (400)
+
+extern int ivtv_debug;
+
+
+struct ivtv_options {
+ int megabytes[IVTV_MAX_STREAMS]; /* Size in megabytes of each stream */
+ int cardtype; /* force card type on load */
+ int tuner; /* set tuner on load */
+ int radio; /* enable/disable radio */
+ int newi2c; /* New I2C algorithm */
+};
+
+#define IVTV_MBOX_DMA_START 6
+#define IVTV_MBOX_DMA_END 8
+#define IVTV_MBOX_DMA 9
+#define IVTV_MBOX_FIELD_DISPLAYED 8
+
+/* ivtv-specific mailbox template */
+struct ivtv_mailbox {
+ u32 flags;
+ u32 cmd;
+ u32 retval;
+ u32 timeout;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+};
+
+struct ivtv_api_cache {
+ unsigned long last_jiffies; /* when last command was issued */
+ u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */
+};
+
+struct ivtv_mailbox_data {
+ volatile struct ivtv_mailbox __iomem *mbox;
+ /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes.
+ If the bit is set, then the corresponding mailbox is in use by the driver. */
+ unsigned long busy;
+ u8 max_mbox;
+};
+
+/* per-buffer bit flags */
+#define IVTV_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */
+
+/* per-stream, s_flags */
+#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */
+#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */
+#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */
+
+#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */
+#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */
+#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */
+#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */
+#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */
+#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */
+
+/* per-ivtv, i_flags */
+#define IVTV_F_I_DMA 0 /* DMA in progress */
+#define IVTV_F_I_UDMA 1 /* UDMA in progress */
+#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */
+#define IVTV_F_I_SPEED_CHANGE 3 /* A speed change is in progress */
+#define IVTV_F_I_EOS 4 /* End of encoder stream reached */
+#define IVTV_F_I_RADIO_USER 5 /* The radio tuner is selected */
+#define IVTV_F_I_DIG_RST 6 /* Reset digitizer */
+#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */
+#define IVTV_F_I_ENC_VBI 8 /* VBI DMA */
+#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */
+#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */
+#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */
+#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */
+#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */
+#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */
+#define IVTV_F_I_WORK_HANDLER_VBI 15 /* there is work to be done for VBI */
+#define IVTV_F_I_WORK_HANDLER_YUV 16 /* there is work to be done for YUV */
+
+/* Event notifications */
+#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */
+#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */
+#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */
+#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */
+
+/* Scatter-Gather array element, used in DMA transfers */
+struct ivtv_SG_element {
+ u32 src;
+ u32 dst;
+ u32 size;
+};
+
+struct ivtv_user_dma {
+ struct mutex lock;
+ int page_count;
+ struct page *map[IVTV_DMA_SG_OSD_ENT];
+
+ /* Base Dev SG Array for cx23415/6 */
+ struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT];
+ dma_addr_t SG_handle;
+ int SG_length;
+
+ /* SG List of Buffers */
+ struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT];
+};
+
+struct ivtv_dma_page_info {
+ unsigned long uaddr;
+ unsigned long first;
+ unsigned long last;
+ unsigned int offset;
+ unsigned int tail;
+ int page_count;
+};
+
+struct ivtv_buffer {
+ struct list_head list;
+ dma_addr_t dma_handle;
+ unsigned long b_flags;
+ char *buf;
+
+ u32 bytesused;
+ u32 readpos;
+};
+
+struct ivtv_queue {
+ struct list_head list;
+ u32 buffers;
+ u32 length;
+ u32 bytesused;
+};
+
+struct ivtv; /* forward reference */
+
+struct ivtv_stream {
+ /* These first four fields are always set, even if the stream
+ is not actually created. */
+ struct video_device *v4l2dev; /* NULL when stream not created */
+ struct ivtv *itv; /* for ease of use */
+ const char *name; /* name of the stream */
+ int type; /* stream type */
+
+ u32 id;
+ spinlock_t qlock; /* locks access to the queues */
+ unsigned long s_flags; /* status flags, see above */
+ int dma; /* can be PCI_DMA_TODEVICE,
+ PCI_DMA_FROMDEVICE or
+ PCI_DMA_NONE */
+ u32 dma_offset;
+ u32 dma_backup;
+ u64 dma_pts;
+
+ int subtype;
+ wait_queue_head_t waitq;
+ u32 dma_last_offset;
+
+ /* Buffer Stats */
+ u32 buffers;
+ u32 buf_size;
+ u32 buffers_stolen;
+
+ /* Buffer Queues */
+ struct ivtv_queue q_free; /* free buffers */
+ struct ivtv_queue q_full; /* full buffers */
+ struct ivtv_queue q_io; /* waiting for I/O */
+ struct ivtv_queue q_dma; /* waiting for DMA */
+ struct ivtv_queue q_predma; /* waiting for DMA */
+
+ /* Base Dev SG Array for cx23415/6 */
+ struct ivtv_SG_element *SGarray;
+ dma_addr_t SG_handle;
+ int SG_length;
+
+ /* SG List of Buffers */
+ struct scatterlist *SGlist;
+};
+
+struct ivtv_open_id {
+ u32 open_id;
+ int type;
+ enum v4l2_priority prio;
+ struct ivtv *itv;
+};
+
+#define IVTV_YUV_UPDATE_HORIZONTAL 0x01
+#define IVTV_YUV_UPDATE_VERTICAL 0x02
+
+struct yuv_frame_info
+{
+ u32 update;
+ int src_x;
+ int src_y;
+ unsigned int src_w;
+ unsigned int src_h;
+ int dst_x;
+ int dst_y;
+ unsigned int dst_w;
+ unsigned int dst_h;
+ int pan_x;
+ int pan_y;
+ u32 vis_w;
+ u32 vis_h;
+ u32 interlaced_y;
+ u32 interlaced_uv;
+ int tru_x;
+ u32 tru_w;
+ u32 tru_h;
+ u32 offset_y;
+};
+
+#define IVTV_YUV_MODE_INTERLACED 0x00
+#define IVTV_YUV_MODE_PROGRESSIVE 0x01
+#define IVTV_YUV_MODE_AUTO 0x02
+#define IVTV_YUV_MODE_MASK 0x03
+
+#define IVTV_YUV_SYNC_EVEN 0x00
+#define IVTV_YUV_SYNC_ODD 0x04
+#define IVTV_YUV_SYNC_MASK 0x04
+
+struct yuv_playback_info
+{
+ u32 reg_2834;
+ u32 reg_2838;
+ u32 reg_283c;
+ u32 reg_2840;
+ u32 reg_2844;
+ u32 reg_2848;
+ u32 reg_2854;
+ u32 reg_285c;
+ u32 reg_2864;
+
+ u32 reg_2870;
+ u32 reg_2874;
+ u32 reg_2890;
+ u32 reg_2898;
+ u32 reg_289c;
+
+ u32 reg_2918;
+ u32 reg_291c;
+ u32 reg_2920;
+ u32 reg_2924;
+ u32 reg_2928;
+ u32 reg_292c;
+ u32 reg_2930;
+
+ u32 reg_2934;
+
+ u32 reg_2938;
+ u32 reg_293c;
+ u32 reg_2940;
+ u32 reg_2944;
+ u32 reg_2948;
+ u32 reg_294c;
+ u32 reg_2950;
+ u32 reg_2954;
+ u32 reg_2958;
+ u32 reg_295c;
+ u32 reg_2960;
+ u32 reg_2964;
+ u32 reg_2968;
+ u32 reg_296c;
+
+ u32 reg_2970;
+
+ int v_filter_1;
+ int v_filter_2;
+ int h_filter;
+
+ u32 osd_x_offset;
+ u32 osd_y_offset;
+
+ u32 osd_x_pan;
+ u32 osd_y_pan;
+
+ u32 osd_vis_w;
+ u32 osd_vis_h;
+
+ int decode_height;
+
+ int frame_interlaced;
+ int frame_interlaced_last;
+
+ int lace_mode;
+ int lace_threshold;
+ int lace_sync_field;
+
+ atomic_t next_dma_frame;
+ atomic_t next_fill_frame;
+
+ u32 yuv_forced_update;
+ int update_frame;
+ struct yuv_frame_info new_frame_info[4];
+ struct yuv_frame_info old_frame_info;
+ struct yuv_frame_info old_frame_info_args;
+
+ void *blanking_ptr;
+ dma_addr_t blanking_dmaptr;
+};
+
+#define IVTV_VBI_FRAMES 32
+
+/* VBI data */
+struct vbi_info {
+ u32 dec_start;
+ u32 enc_start, enc_size;
+ int fpi;
+ u32 frame;
+ u32 dma_offset;
+ u8 cc_data_odd[256];
+ u8 cc_data_even[256];
+ int cc_pos;
+ u8 cc_no_update;
+ u8 vps[5];
+ u8 vps_found;
+ int wss;
+ u8 wss_found;
+ u8 wss_no_update;
+ u32 raw_decoder_line_size;
+ u8 raw_decoder_sav_odd_field;
+ u8 raw_decoder_sav_even_field;
+ u32 sliced_decoder_line_size;
+ u8 sliced_decoder_sav_odd_field;
+ u8 sliced_decoder_sav_even_field;
+ struct v4l2_format in;
+ /* convenience pointer to sliced struct in vbi_in union */
+ struct v4l2_sliced_vbi_format *sliced_in;
+ u32 service_set_in;
+ u32 service_set_out;
+ int insert_mpeg;
+
+ /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
+ One for /dev/vbi0 and one for /dev/vbi8 */
+ struct v4l2_sliced_vbi_data sliced_data[36];
+ struct v4l2_sliced_vbi_data sliced_dec_data[36];
+
+ /* Buffer for VBI data inserted into MPEG stream.
+ The first byte is a dummy byte that's never used.
+ The next 16 bytes contain the MPEG header for the VBI data,
+ the remainder is the actual VBI data.
+ The max size accepted by the MPEG VBI reinsertion turns out
+ to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
+ where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
+ a single line header byte and 2 * 18 is the number of VBI lines per frame.
+
+ However, it seems that the data must be 1K aligned, so we have to
+ pad the data until the 1 or 2 K boundary.
+
+ This pointer array will allocate 2049 bytes to store each VBI frame. */
+ u8 *sliced_mpeg_data[IVTV_VBI_FRAMES];
+ u32 sliced_mpeg_size[IVTV_VBI_FRAMES];
+ struct ivtv_buffer sliced_mpeg_buf;
+ u32 inserted_frame;
+
+ u32 start[2], count;
+ u32 raw_size;
+ u32 sliced_size;
+};
+
+/* forward declaration of struct defined in ivtv-cards.h */
+struct ivtv_card;
+
+/* Struct to hold info about ivtv cards */
+struct ivtv {
+ int num; /* board number, -1 during init! */
+ char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */
+ struct pci_dev *dev; /* PCI device */
+ const struct ivtv_card *card; /* card information */
+ const char *card_name; /* full name of the card */
+ u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */
+ u8 is_50hz;
+ u8 is_60hz;
+ u8 is_out_50hz;
+ u8 is_out_60hz;
+ u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */
+ u8 nof_inputs; /* number of video inputs */
+ u8 nof_audio_inputs; /* number of audio inputs */
+ u32 v4l2_cap; /* V4L2 capabilities of card */
+ u32 hw_flags; /* Hardware description of the board */
+
+ /* controlling Video decoder function */
+ int (*video_dec_func)(struct ivtv *, unsigned int, void *);
+
+ struct ivtv_options options; /* User options */
+ int stream_buf_size[IVTV_MAX_STREAMS]; /* Stream buffer size */
+ struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* Stream data */
+ int speed;
+ u8 speed_mute_audio;
+ unsigned long i_flags; /* global ivtv flags */
+ atomic_t capturing; /* count number of active capture streams */
+ atomic_t decoding; /* count number of active decoding streams */
+ u32 irq_rr_idx; /* Round-robin stream index */
+ int cur_dma_stream; /* index of stream doing DMA */
+ u32 dma_data_req_offset;
+ u32 dma_data_req_size;
+ int output_mode; /* NONE, MPG, YUV, UDMA YUV, passthrough */
+ spinlock_t lock; /* lock access to this struct */
+ int search_pack_header;
+
+ spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
+
+ /* User based DMA for OSD */
+ struct ivtv_user_dma udma;
+
+ int open_id; /* incremented each time an open occurs, used as unique ID.
+ starts at 1, so 0 can be used as uninitialized value
+ in the stream->id. */
+
+ u32 base_addr;
+ u32 irqmask;
+
+ struct v4l2_prio_state prio;
+ struct workqueue_struct *irq_work_queues;
+ struct work_struct irq_work_queue;
+ struct timer_list dma_timer; /* Timer used to catch unfinished DMAs */
+
+ struct vbi_info vbi;
+
+ struct ivtv_mailbox_data enc_mbox;
+ struct ivtv_mailbox_data dec_mbox;
+ struct ivtv_api_cache api_cache[256]; /* Cached API Commands */
+
+ u8 card_rev;
+ volatile void __iomem *enc_mem, *dec_mem, *reg_mem;
+
+ u32 pgm_info_offset;
+ u32 pgm_info_num;
+ u32 pgm_info_write_idx;
+ u32 pgm_info_read_idx;
+ struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX];
+
+ u64 mpg_data_received;
+ u64 vbi_data_inserted;
+
+ wait_queue_head_t cap_w;
+ /* when the next decoder event arrives this queue is woken up */
+ wait_queue_head_t event_waitq;
+ /* when the next decoder vsync arrives this queue is woken up */
+ wait_queue_head_t vsync_waitq;
+ /* when the current DMA is finished this queue is woken up */
+ wait_queue_head_t dma_waitq;
+
+ /* OSD support */
+ unsigned long osd_video_pbase;
+ int osd_global_alpha_state; /* 0=off : 1=on */
+ int osd_local_alpha_state; /* 0=off : 1=on */
+ int osd_color_key_state; /* 0=off : 1=on */
+ u8 osd_global_alpha; /* Current global alpha */
+ u32 osd_color_key; /* Current color key */
+ u32 osd_pixelformat; /* Current pixel format */
+ struct v4l2_rect osd_rect; /* Current OSD position and size */
+ struct v4l2_rect main_rect; /* Current Main window position and size */
+
+ u32 last_dec_timing[3]; /* Store last retrieved pts/scr/frame values */
+
+ /* i2c */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ struct mutex i2c_bus_lock;
+ int i2c_state;
+ struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
+
+ /* v4l2 and User settings */
+
+ /* codec settings */
+ struct cx2341x_mpeg_params params;
+ u32 audio_input;
+ u32 active_input;
+ u32 active_output;
+ v4l2_std_id std;
+ v4l2_std_id std_out;
+ v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
+ u8 audio_stereo_mode;
+ u8 audio_bilingual_mode;
+
+ /* dualwatch */
+ unsigned long dualwatch_jiffies;
+ u16 dualwatch_stereo_mode;
+
+ /* Digitizer type */
+ int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
+
+ u32 lastVsyncFrame;
+
+ struct yuv_playback_info yuv_info;
+ struct osd_info *osd_info;
+};
+
+/* Globals */
+extern struct ivtv *ivtv_cards[];
+extern int ivtv_cards_active;
+extern int ivtv_first_minor;
+extern spinlock_t ivtv_cards_lock;
+
+/*==============Prototypes==================*/
+
+/* Hardware/IRQ */
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask);
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask);
+
+/* try to set output mode, return current mode. */
+int ivtv_set_output_mode(struct ivtv *itv, int mode);
+
+/* return current output stream based on current mode */
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv);
+
+/* Return non-zero if a signal is pending */
+int ivtv_sleep_timeout(int timeout, int intr);
+
+/* Wait on queue, returns -EINTR if interrupted */
+int ivtv_waitq(wait_queue_head_t *waitq);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv);
+
+/* This is a PCI post thing, where if the pci register is not read, then
+ the write doesn't always take effect right away. By reading back the
+ register any pending PCI writes will be performed (in order), and so
+ you can be sure that the writes are guaranteed to be done.
+
+ Rarely needed, only in some timing sensitive cases.
+ Apparently if this is not done some motherboards seem
+ to kill the firmware and get into the broken state until computer is
+ rebooted. */
+#define write_sync(val, reg) \
+ do { writel(val, reg); readl(reg); } while (0)
+
+#define read_reg(reg) readl(itv->reg_mem + (reg))
+#define write_reg(val, reg) writel(val, itv->reg_mem + (reg))
+#define write_reg_sync(val, reg) \
+ do { write_reg(val, reg); read_reg(reg); } while (0)
+
+#define read_enc(addr) readl(itv->enc_mem + (u32)(addr))
+#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr))
+#define write_enc_sync(val, addr) \
+ do { write_enc(val, addr); read_enc(addr); } while (0)
+
+#define read_dec(addr) readl(itv->dec_mem + (u32)(addr))
+#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr))
+#define write_dec_sync(val, addr) \
+ do { write_dec(val, addr); read_dec(addr); } while (0)
+
+#endif /* IVTV_DRIVER_H */
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
new file mode 100644
index 000000000000..1637097ddec7
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -0,0 +1,921 @@
+/*
+ file operation functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-vbi.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-audio.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-controls.h"
+#include "ivtv-ioctl.h"
+
+/* This function tries to claim the stream for a specific file descriptor.
+ If no one else is using this stream then the stream is claimed and
+ associated VBI streams are also automatically claimed.
+ Possible error returns: -EBUSY if someone else has claimed
+ the stream or 0 on success. */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[type];
+ struct ivtv_stream *s_vbi;
+ int vbi_type;
+
+ if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ /* someone already claimed this stream */
+ if (s->id == id->open_id) {
+ /* yes, this file descriptor did. So that's OK. */
+ return 0;
+ }
+ if (s->id == -1 && (type == IVTV_DEC_STREAM_TYPE_VBI ||
+ type == IVTV_ENC_STREAM_TYPE_VBI)) {
+ /* VBI is handled already internally, now also assign
+ the file descriptor to this stream for external
+ reading of the stream. */
+ s->id = id->open_id;
+ IVTV_DEBUG_INFO("Start Read VBI\n");
+ return 0;
+ }
+ /* someone else is using this stream already */
+ IVTV_DEBUG_INFO("Stream %d is busy\n", type);
+ return -EBUSY;
+ }
+ s->id = id->open_id;
+ if (type == IVTV_DEC_STREAM_TYPE_VBI) {
+ /* Enable reinsertion interrupt */
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ }
+
+ /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI,
+ IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI
+ (provided VBI insertion is on and sliced VBI is selected), for all
+ other streams we're done */
+ if (type == IVTV_DEC_STREAM_TYPE_MPG) {
+ vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
+ } else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
+ itv->vbi.insert_mpeg && itv->vbi.sliced_in->service_set) {
+ vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
+ } else {
+ return 0;
+ }
+ s_vbi = &itv->streams[vbi_type];
+
+ if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) {
+ /* Enable reinsertion interrupt */
+ if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ }
+ /* mark that it is used internally */
+ set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags);
+ return 0;
+}
+
+/* This function releases a previously claimed stream. It will take into
+ account associated VBI streams. */
+void ivtv_release_stream(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi;
+
+ s->id = -1;
+ if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+ /* this stream is still in use internally */
+ return;
+ }
+ if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+ return;
+ }
+
+ ivtv_flush_queues(s);
+
+ /* disable reinsertion interrupt */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+
+ /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI,
+ IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI,
+ for all other streams we're done */
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+ else if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ else
+ return;
+
+ /* clear internal use flag */
+ if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
+ /* was already cleared */
+ return;
+ }
+ if (s_vbi->id != -1) {
+ /* VBI stream still claimed by a file descriptor */
+ return;
+ }
+ /* disable reinsertion interrupt */
+ if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags);
+ ivtv_flush_queues(s_vbi);
+}
+
+static void ivtv_dualwatch(struct ivtv *itv)
+{
+ struct v4l2_tuner vt;
+ u16 new_bitmap;
+ u16 new_stereo_mode;
+ const u16 stereo_mask = 0x0300;
+ const u16 dual = 0x0200;
+
+ new_stereo_mode = itv->params.audio_properties & stereo_mask;
+ memset(&vt, 0, sizeof(vt));
+ ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt);
+ if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+ new_stereo_mode = dual;
+
+ if (new_stereo_mode == itv->dualwatch_stereo_mode)
+ return;
+
+ new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
+
+ IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
+ itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
+
+ if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
+ itv->dualwatch_stereo_mode = new_stereo_mode;
+ return;
+ }
+ IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+static void ivtv_update_pgm_info(struct ivtv *itv)
+{
+ u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24;
+ int cnt;
+ int i = 0;
+
+ if (wr_idx >= itv->pgm_info_num) {
+ IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num);
+ return;
+ }
+ cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num;
+ while (i < cnt) {
+ int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+ struct v4l2_enc_idx_entry *e = itv->pgm_info + idx;
+ u32 addr = itv->pgm_info_offset + 4 + idx * 24;
+ const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 };
+
+ e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32);
+ if (e->offset > itv->mpg_data_received) {
+ break;
+ }
+ e->offset += itv->vbi_data_inserted;
+ e->length = read_enc(addr);
+ e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32);
+ e->flags = mapping[read_enc(addr + 12) & 3];
+ i++;
+ }
+ itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+}
+
+static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ struct ivtv_buffer *buf;
+ DEFINE_WAIT(wait);
+
+ *err = 0;
+ while (1) {
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG) {
+ /* Process pending program info updates and pending VBI data */
+ ivtv_update_pgm_info(itv);
+
+ if (jiffies - itv->dualwatch_jiffies > HZ) {
+ itv->dualwatch_jiffies = jiffies;
+ ivtv_dualwatch(itv);
+ }
+
+ if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+ while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) {
+ /* byteswap and process VBI data */
+ ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type);
+ ivtv_enqueue(s_vbi, buf, &s_vbi->q_free);
+ }
+ }
+ buf = &itv->vbi.sliced_mpeg_buf;
+ if (buf->readpos != buf->bytesused) {
+ return buf;
+ }
+ }
+
+ /* do we have leftover data? */
+ buf = ivtv_dequeue(s, &s->q_io);
+ if (buf)
+ return buf;
+
+ /* do we have new data? */
+ buf = ivtv_dequeue(s, &s->q_full);
+ if (buf) {
+ if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags))
+ return buf;
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ /* byteswap MPG data */
+ ivtv_buf_swap(buf);
+ else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) {
+ /* byteswap and process VBI data */
+ ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type);
+ }
+ return buf;
+ }
+ /* return if file was opened with O_NONBLOCK */
+ if (non_block) {
+ *err = -EAGAIN;
+ return NULL;
+ }
+
+ /* return if end of stream */
+ if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ IVTV_DEBUG_INFO("EOS %s\n", s->name);
+ return NULL;
+ }
+
+ /* wait for more data to arrive */
+ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+ /* New buffers might have become available before we were added to the waitqueue */
+ if (!s->q_full.buffers)
+ schedule();
+ finish_wait(&s->waitq, &wait);
+ if (signal_pending(current)) {
+ /* return if a signal was received */
+ IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+ *err = -EINTR;
+ return NULL;
+ }
+ }
+}
+
+static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv)
+{
+ int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+
+ itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx];
+ itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx];
+ itv->vbi.sliced_mpeg_buf.readpos = 0;
+}
+
+static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf,
+ char __user *ubuf, size_t ucount)
+{
+ struct ivtv *itv = s->itv;
+ size_t len = buf->bytesused - buf->readpos;
+
+ if (len > ucount) len = ucount;
+ if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ itv->vbi.sliced_in->service_set && buf != &itv->vbi.sliced_mpeg_buf) {
+ const char *start = buf->buf + buf->readpos;
+ const char *p = start + 1;
+ const u8 *q;
+ u8 ch = itv->search_pack_header ? 0xba : 0xe0;
+ int stuffing, i;
+
+ while (start + len > p && (q = memchr(p, 0, start + len - p))) {
+ p = q + 1;
+ if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+ q[1] != 0 || q[2] != 1 || q[3] != ch) {
+ continue;
+ }
+ if (!itv->search_pack_header) {
+ if ((q[6] & 0xc0) != 0x80)
+ continue;
+ if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) ||
+ ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) {
+ ch = 0xba;
+ itv->search_pack_header = 1;
+ p = q + 9;
+ }
+ continue;
+ }
+ stuffing = q[13] & 7;
+ /* all stuffing bytes must be 0xff */
+ for (i = 0; i < stuffing; i++)
+ if (q[14 + i] != 0xff)
+ break;
+ if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 &&
+ q[14 + stuffing] == 0 && q[15 + stuffing] == 0 &&
+ q[16 + stuffing] == 1) {
+ itv->search_pack_header = 0;
+ len = (char *)q - start;
+ ivtv_setup_sliced_vbi_buf(itv);
+ break;
+ }
+ }
+ }
+ if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+ IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name);
+ return -EFAULT;
+ }
+ /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount,
+ buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len,
+ buf == &itv->vbi.sliced_mpeg_buf); */
+ buf->readpos += len;
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf)
+ itv->mpg_data_received += len;
+ return len;
+}
+
+static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block)
+{
+ struct ivtv *itv = s->itv;
+ size_t tot_written = 0;
+ int single_frame = 0;
+
+ if (atomic_read(&itv->capturing) == 0 && s->id == -1) {
+ /* shouldn't happen */
+ IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name);
+ return -EIO;
+ }
+
+ /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
+ arrive one-by-one, so make sure we never output more than one VBI frame at a time */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
+ (s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set))
+ single_frame = 1;
+
+ for (;;) {
+ struct ivtv_buffer *buf;
+ int rc;
+
+ buf = ivtv_get_buffer(s, non_block, &rc);
+ if (buf == NULL && rc == -EAGAIN && tot_written)
+ break;
+ if (buf == NULL)
+ return rc;
+ rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
+ if (buf != &itv->vbi.sliced_mpeg_buf) {
+ ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
+ }
+ else if (buf->readpos == buf->bytesused) {
+ int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+ itv->vbi.sliced_mpeg_size[idx] = 0;
+ itv->vbi.inserted_frame++;
+ itv->vbi_data_inserted += buf->bytesused;
+ }
+ if (rc < 0)
+ return rc;
+ tot_written += rc;
+
+ if (tot_written == tot_count || single_frame)
+ break;
+ }
+ return tot_written;
+}
+
+static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count,
+ loff_t *pos, int non_block)
+{
+ ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
+ struct ivtv *itv = s->itv;
+
+ IVTV_DEBUG_INFO("read %zd from %s, got %zd\n", count, s->name, rc);
+ if (rc > 0)
+ pos += rc;
+ return rc;
+}
+
+int ivtv_start_capture(struct ivtv_open_id *id)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ struct ivtv_stream *s_vbi;
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_RAD ||
+ s->type == IVTV_DEC_STREAM_TYPE_MPG ||
+ s->type == IVTV_DEC_STREAM_TYPE_YUV ||
+ s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+ /* you cannot read from these stream types. */
+ return -EPERM;
+ }
+
+ /* Try to claim this stream. */
+ if (ivtv_claim_stream(id, s->type))
+ return -EBUSY;
+
+ /* This stream does not need to start capturing */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return 0;
+ }
+
+ /* If capture is already in progress, then we also have to
+ do nothing extra. */
+ if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return 0;
+ }
+
+ /* Start VBI capture if required */
+ s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+ /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed
+ automatically when the MPG stream is claimed.
+ We only need to start the VBI capturing. */
+ if (ivtv_start_v4l2_encode_stream(s_vbi)) {
+ IVTV_DEBUG_WARN("VBI capture start failed\n");
+
+ /* Failure, clean up and return an error */
+ clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ /* also releases the associated VBI stream */
+ ivtv_release_stream(s);
+ return -EIO;
+ }
+ IVTV_DEBUG_INFO("VBI insertion started\n");
+ }
+
+ /* Tell the card to start capturing */
+ if (!ivtv_start_v4l2_encode_stream(s)) {
+ /* We're done */
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ /* Resume a possibly paused encoder */
+ if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+ return 0;
+ }
+
+ /* failure, clean up */
+ IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+ /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released
+ automatically when the MPG stream is released.
+ We only need to stop the VBI capturing. */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+ ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+ clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+ }
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ ivtv_release_stream(s);
+ return -EIO;
+}
+
+ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos)
+{
+ struct ivtv_open_id *id = filp->private_data;
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int rc;
+
+ IVTV_DEBUG_IOCTL("read %zd bytes from %s\n", count, s->name);
+
+ rc = ivtv_start_capture(id);
+ if (rc)
+ return rc;
+ return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+}
+
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ if (atomic_read(&itv->decoding) == 0) {
+ if (ivtv_claim_stream(id, s->type)) {
+ /* someone else is using this stream already */
+ IVTV_DEBUG_WARN("start decode, stream already claimed\n");
+ return -EBUSY;
+ }
+ ivtv_start_v4l2_decode_stream(s, 0);
+ }
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ return ivtv_set_speed(itv, speed);
+ return 0;
+}
+
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
+{
+ struct ivtv_open_id *id = filp->private_data;
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ struct ivtv_buffer *buf;
+ struct ivtv_queue q;
+ int bytes_written = 0;
+ int mode;
+ int rc;
+ DEFINE_WAIT(wait);
+
+ IVTV_DEBUG_IOCTL("write %zd bytes to %s\n", count, s->name);
+
+ if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
+ s->type != IVTV_DEC_STREAM_TYPE_YUV &&
+ s->type != IVTV_DEC_STREAM_TYPE_VOUT)
+ /* not decoder streams */
+ return -EPERM;
+
+ /* Try to claim this stream */
+ if (ivtv_claim_stream(id, s->type))
+ return -EBUSY;
+
+ /* This stream does not need to start any decoding */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return ivtv_write_vbi(itv, user_buf, count);
+ }
+
+ mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
+
+ if (ivtv_set_output_mode(itv, mode) != mode) {
+ ivtv_release_stream(s);
+ return -EBUSY;
+ }
+ ivtv_queue_init(&q);
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+
+retry:
+ for (;;) {
+ /* Gather buffers */
+ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
+ ivtv_enqueue(s, buf, &q);
+ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) {
+ ivtv_enqueue(s, buf, &q);
+ }
+ if (q.buffers)
+ break;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+ /* New buffers might have become free before we were added to the waitqueue */
+ if (!s->q_free.buffers)
+ schedule();
+ finish_wait(&s->waitq, &wait);
+ if (signal_pending(current)) {
+ IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+ return -EINTR;
+ }
+ }
+
+ /* copy user data into buffers */
+ while ((buf = ivtv_dequeue(s, &q))) {
+ /* Make sure we really got all the user data */
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+
+ if (rc < 0) {
+ ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
+ return rc;
+ }
+ user_buf += rc;
+ count -= rc;
+ bytes_written += rc;
+
+ if (buf->bytesused != s->buf_size) {
+ /* incomplete, leave in q_io for next time */
+ ivtv_enqueue(s, buf, &s->q_io);
+ break;
+ }
+ /* Byteswap MPEG buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_buf_swap(buf);
+ ivtv_enqueue(s, buf, &s->q_full);
+ }
+
+ /* Start decoder (returns 0 if already started) */
+ rc = ivtv_start_decoding(id, itv->speed);
+ if (rc) {
+ IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
+
+ /* failure, clean up */
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return rc;
+ }
+ if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
+ if (s->q_full.length >= itv->dma_data_req_size) {
+ int got_sig;
+
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ while (!(got_sig = signal_pending(current)) &&
+ test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+ if (got_sig) {
+ IVTV_DEBUG_INFO("User interrupted %s\n", s->name);
+ return -EINTR;
+ }
+
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+ ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1);
+ }
+ }
+ /* more user data is available, wait until buffers become free
+ to transfer the rest. */
+ if (count && !(filp->f_flags & O_NONBLOCK))
+ goto retry;
+ IVTV_DEBUG_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+ return bytes_written;
+}
+
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
+{
+ struct ivtv_open_id *id = filp->private_data;
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int res = 0;
+
+ /* add stream's waitq to the poll list */
+ poll_wait(filp, &s->waitq, wait);
+
+ set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+ if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) ||
+ test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+ res = POLLPRI;
+
+ /* Allow write if buffers are available for writing */
+ if (s->q_free.buffers)
+ res |= POLLOUT | POLLWRNORM;
+ return res;
+}
+
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
+{
+ struct ivtv_open_id *id = filp->private_data;
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ /* Start a capture if there is none */
+ if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ int rc = ivtv_start_capture(id);
+
+ if (rc) {
+ IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
+ s->name, rc);
+ return POLLERR;
+ }
+ }
+
+ /* add stream's waitq to the poll list */
+ poll_wait(filp, &s->waitq, wait);
+
+ if (eof || s->q_full.length)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+ /* 'Unclaim' this stream */
+
+ /* Stop capturing */
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+ IVTV_DEBUG_INFO("close stopping capture\n");
+ /* Special case: a running VBI capture for VBI insertion
+ in the mpeg stream. Need to stop that too. */
+ if (id->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) &&
+ !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+ IVTV_DEBUG_INFO("close stopping embedded VBI capture\n");
+ ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+ }
+ if ((id->type == IVTV_DEC_STREAM_TYPE_VBI ||
+ id->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+ /* Also used internally, don't stop capturing */
+ s->id = -1;
+ }
+ else {
+ ivtv_stop_v4l2_encode_stream(s, gop_end);
+ }
+ }
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ ivtv_release_stream(s);
+}
+
+static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+ /* Stop decoding */
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ IVTV_DEBUG_INFO("close stopping decode\n");
+
+ ivtv_stop_v4l2_decode_stream(s, flags, pts);
+ }
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ if (id->type == IVTV_DEC_STREAM_TYPE_YUV && test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) {
+ /* Restore registers we've changed & clean up any mess we've made */
+ ivtv_yuv_close(itv);
+ }
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_YUV)
+ itv->output_mode = OUT_NONE;
+ else if (s->type == IVTV_DEC_STREAM_TYPE_MPG && itv->output_mode == OUT_MPG)
+ itv->output_mode = OUT_NONE;
+
+ itv->speed = 0;
+ ivtv_release_stream(s);
+}
+
+int ivtv_v4l2_close(struct inode *inode, struct file *filp)
+{
+ struct ivtv_open_id *id = filp->private_data;
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+ v4l2_prio_close(&itv->prio, &id->prio);
+
+ /* Easy case first: this stream was never claimed by us */
+ if (s->id != id->open_id) {
+ kfree(id);
+ return 0;
+ }
+
+ /* 'Unclaim' this stream */
+
+ /* Stop radio */
+ if (id->type == IVTV_ENC_STREAM_TYPE_RAD) {
+ /* Closing radio device, return to TV mode */
+ ivtv_mute(itv);
+ /* Mark that the radio is no longer in use */
+ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+ /* Switch tuner to TV */
+ ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+ /* Select correct audio input (i.e. TV tuner or Line in) */
+ ivtv_audio_set_io(itv);
+ /* Done! Unmute and continue. */
+ ivtv_unmute(itv);
+ ivtv_release_stream(s);
+ } else if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+ ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
+ } else {
+ ivtv_stop_capture(id, 0);
+ }
+ kfree(id);
+ return 0;
+}
+
+int ivtv_v4l2_open(struct inode *inode, struct file *filp)
+{
+ int x, y = 0;
+ struct ivtv_open_id *item;
+ struct ivtv *itv = NULL;
+ struct ivtv_stream *s = NULL;
+ int minor = MINOR(inode->i_rdev);
+
+ /* Find which card this open was on */
+ spin_lock(&ivtv_cards_lock);
+ for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
+ /* find out which stream this open was on */
+ for (y = 0; y < IVTV_MAX_STREAMS; y++) {
+ s = &ivtv_cards[x]->streams[y];
+ if (s->v4l2dev && s->v4l2dev->minor == minor) {
+ itv = ivtv_cards[x];
+ break;
+ }
+ }
+ }
+ spin_unlock(&ivtv_cards_lock);
+
+ if (itv == NULL) {
+ /* Couldn't find a device registered
+ on that minor, shouldn't happen! */
+ printk(KERN_WARNING "ivtv: no ivtv device found on minor %d\n", minor);
+ return -ENXIO;
+ }
+
+ if (y == IVTV_DEC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
+ return -EBUSY;
+
+ if (y == IVTV_DEC_STREAM_TYPE_YUV &&
+ test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
+ return -EBUSY;
+
+ if (y == IVTV_DEC_STREAM_TYPE_YUV) {
+ if (read_reg(0x82c) == 0) {
+ IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
+ /* return -ENODEV; */
+ }
+ ivtv_udma_alloc(itv);
+ }
+
+ /* Allocate memory */
+ item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
+ if (NULL == item) {
+ IVTV_DEBUG_WARN("nomem on v4l2 open\n");
+ return -ENOMEM;
+ }
+ item->itv = itv;
+ item->type = y;
+ v4l2_prio_open(&itv->prio, &item->prio);
+
+ item->open_id = itv->open_id++;
+ filp->private_data = item;
+
+ if (item->type == IVTV_ENC_STREAM_TYPE_RAD) {
+ /* Try to claim this stream */
+ if (ivtv_claim_stream(item, item->type)) {
+ /* No, it's already in use */
+ kfree(item);
+ return -EBUSY;
+ }
+
+ /* We have the radio */
+ ivtv_mute(itv);
+ /* Switch tuner to radio */
+ ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
+ /* Mark that the radio is being used. */
+ set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+ /* Select the correct audio input (i.e. radio tuner) */
+ ivtv_audio_set_io(itv);
+ /* Done! Unmute and continue. */
+ ivtv_unmute(itv);
+ }
+
+ /* YUV or MPG Decoding Mode? */
+ if (y == IVTV_DEC_STREAM_TYPE_MPG)
+ clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+ else if (y == IVTV_DEC_STREAM_TYPE_YUV)
+ {
+ set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+ }
+
+ return 0;
+}
+
+void ivtv_mute(struct ivtv *itv)
+{
+ struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 1 };
+
+ /* Mute sound to avoid pop */
+ ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
+
+ if (atomic_read(&itv->capturing))
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
+
+ IVTV_DEBUG_INFO("Mute\n");
+}
+
+void ivtv_unmute(struct ivtv *itv)
+{
+ struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 };
+
+ /* initialize or refresh input */
+ if (atomic_read(&itv->capturing) == 0)
+ ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+ ivtv_sleep_timeout(HZ / 10, 0);
+
+ if (atomic_read(&itv->capturing)) {
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
+ }
+
+ /* Unmute */
+ ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
+ IVTV_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h
new file mode 100644
index 000000000000..74a1745fabbc
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-fileops.h
@@ -0,0 +1,44 @@
+/*
+ file operation functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+/* Testing/Debugging */
+int ivtv_v4l2_open(struct inode *inode, struct file *filp);
+ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t * pos);
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+ loff_t * pos);
+int ivtv_v4l2_close(struct inode *inode, struct file *filp);
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait);
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait);
+int ivtv_start_capture(struct ivtv_open_id *id);
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end);
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed);
+void ivtv_mute(struct ivtv *itv);
+void ivtv_unmute(struct ivtv *itv);
+
+/* Utilities */
+
+/* Try to claim a stream for the filehandle. Return 0 on success,
+ -EBUSY if stream already claimed. Once a stream is claimed, it
+ remains claimed until the associated filehandle is closed. */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type);
+
+/* Release a previously claimed stream. */
+void ivtv_release_stream(struct ivtv_stream *s);
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
new file mode 100644
index 000000000000..d4c910b782af
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-firmware.c
@@ -0,0 +1,272 @@
+/*
+ ivtv firmware functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-firmware.h"
+#include <linux/firmware.h>
+
+#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE
+#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6
+#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB
+#define IVTV_CMD_VDM_STOP 0x00000000
+#define IVTV_CMD_AO_STOP 0x00000005
+#define IVTV_CMD_APU_PING 0x00000000
+#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE
+#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+#define IVTV_CMD_SPU_STOP 0x00000001
+#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A
+#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640
+#define IVTV_SDRAM_SLEEPTIME (60 * HZ / 100) /* 600 ms */
+
+#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg"
+#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024)
+
+/* Encoder/decoder firmware sizes */
+#define IVTV_FW_ENC_SIZE (376836)
+#define IVTV_FW_DEC_SIZE (256*1024)
+
+static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size)
+{
+ const struct firmware *fw = NULL;
+ int retries = 3;
+
+retry:
+ if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) {
+ int i;
+ volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
+ const u32 *src = (const u32 *)fw->data;
+
+ /* temporarily allow 256 KB encoding firmwares as well for
+ compatibility with blackbird cards */
+ if (fw->size != size && fw->size != 256 * 1024) {
+ /* Due to race conditions in firmware loading (esp. with udev <0.95)
+ the wrong file was sometimes loaded. So we check filesizes to
+ see if at least the right-sized file was loaded. If not, then we
+ retry. */
+ IVTV_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
+ release_firmware(fw);
+ retries--;
+ goto retry;
+ }
+ for (i = 0; i < fw->size; i += 4) {
+ /* no need for endianness conversion on the ppc */
+ __raw_writel(*src, dst);
+ dst++;
+ src++;
+ }
+ release_firmware(fw);
+ IVTV_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+ return size;
+ }
+ IVTV_ERR("unable to open firmware %s (must be %ld bytes)\n", fn, size);
+ IVTV_ERR("did you put the firmware in the hotplug firmware directory?\n");
+ return -ENOMEM;
+}
+
+void ivtv_halt_firmware(struct ivtv *itv)
+{
+ IVTV_DEBUG_INFO("Preparing for firmware halt.\n");
+ if (itv->has_cx23415 && itv->dec_mbox.mbox)
+ ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0);
+ if (itv->enc_mbox.mbox)
+ ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0);
+
+ ivtv_sleep_timeout(HZ / 100, 0);
+ itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL;
+
+ IVTV_DEBUG_INFO("Stopping VDM\n");
+ write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM);
+
+ IVTV_DEBUG_INFO("Stopping AO\n");
+ write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO);
+
+ IVTV_DEBUG_INFO("pinging (?) APU\n");
+ write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU);
+
+ IVTV_DEBUG_INFO("Stopping VPU\n");
+ if (!itv->has_cx23415)
+ write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU);
+ else
+ write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU);
+
+ IVTV_DEBUG_INFO("Resetting Hw Blocks\n");
+ write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS);
+
+ IVTV_DEBUG_INFO("Stopping SPU\n");
+ write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU);
+
+ ivtv_sleep_timeout(HZ / 100, 0);
+
+ IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n");
+ write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE);
+
+ IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n");
+ write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH);
+
+ if (itv->has_cx23415) {
+ IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n");
+ write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE);
+
+ IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n");
+ write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH);
+ }
+
+ IVTV_DEBUG_INFO("Sleeping for %dms (600 recommended)\n",
+ (int)(IVTV_SDRAM_SLEEPTIME * 1000 / HZ));
+ ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME, 0);
+}
+
+void ivtv_firmware_versions(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ /* Encoder */
+ ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0);
+ IVTV_INFO("Encoder revision: 0x%08x\n", data[0]);
+
+ if (data[0] != 0x02060039)
+ IVTV_WARN("Recommended firmware version is 0x02060039.\n");
+
+ if (itv->has_cx23415) {
+ /* Decoder */
+ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0);
+ IVTV_INFO("Decoder revision: 0x%08x\n", data[0]);
+ }
+}
+
+static int ivtv_firmware_copy(struct ivtv *itv)
+{
+ IVTV_DEBUG_INFO("Loading encoder image\n");
+ if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME,
+ itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) {
+ IVTV_DEBUG_WARN("failed loading encoder firmware\n");
+ return -3;
+ }
+ if (!itv->has_cx23415)
+ return 0;
+
+ IVTV_DEBUG_INFO("Loading decoder image\n");
+ if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME,
+ itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) {
+ IVTV_DEBUG_WARN("failed loading decoder firmware\n");
+ return -1;
+ }
+ return 0;
+}
+
+static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size)
+{
+ int i;
+
+ /* mailbox is preceeded by a 16 byte 'magic cookie' starting at a 256-byte
+ address boundary */
+ for (i = 0; i < size; i += 0x100) {
+ if (readl(mem + i) == 0x12345678 &&
+ readl(mem + i + 4) == 0x34567812 &&
+ readl(mem + i + 8) == 0x56781234 &&
+ readl(mem + i + 12) == 0x78123456) {
+ return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16);
+ }
+ }
+ return NULL;
+}
+
+int ivtv_firmware_init(struct ivtv *itv)
+{
+ int err;
+
+ ivtv_halt_firmware(itv);
+
+ /* load firmware */
+ err = ivtv_firmware_copy(itv);
+ if (err) {
+ IVTV_DEBUG_WARN("Error %d loading firmware\n", err);
+ return err;
+ }
+
+ /* start firmware */
+ write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU);
+ ivtv_sleep_timeout(HZ / 10, 0);
+ if (itv->has_cx23415)
+ write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU);
+ else
+ write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU);
+ ivtv_sleep_timeout(HZ / 10, 0);
+
+ /* find mailboxes and ping firmware */
+ itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE);
+ if (itv->enc_mbox.mbox == NULL)
+ IVTV_ERR("Encoder mailbox not found\n");
+ else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) {
+ IVTV_ERR("Encoder firmware dead!\n");
+ itv->enc_mbox.mbox = NULL;
+ }
+ if (itv->enc_mbox.mbox == NULL)
+ return -ENODEV;
+
+ if (!itv->has_cx23415)
+ return 0;
+
+ itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE);
+ if (itv->dec_mbox.mbox == NULL)
+ IVTV_ERR("Decoder mailbox not found\n");
+ else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) {
+ IVTV_ERR("Decoder firmware dead!\n");
+ itv->dec_mbox.mbox = NULL;
+ }
+ return itv->dec_mbox.mbox ? 0 : -ENODEV;
+}
+
+void ivtv_init_mpeg_decoder(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ long readbytes;
+ volatile u8 __iomem *mem_offset;
+
+ data[0] = 0;
+ data[1] = itv->params.width; /* YUV source width */
+ data[2] = itv->params.height;
+ data[3] = itv->params.audio_properties; /* Audio settings to use,
+ bitmap. see docs. */
+ if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
+ IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
+ return;
+ }
+
+ if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) {
+ IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
+ return;
+ }
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
+ mem_offset = itv->dec_mem + data[1];
+
+ if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
+ mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) {
+ IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n",
+ IVTV_DECODE_INIT_MPEG_FILENAME);
+ } else {
+ ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0);
+ ivtv_sleep_timeout(HZ / 10, 0);
+ }
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
+}
diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h
new file mode 100644
index 000000000000..8b2ffe658905
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-firmware.h
@@ -0,0 +1,25 @@
+/*
+ ivtv firmware functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+int ivtv_firmware_init(struct ivtv *itv);
+void ivtv_firmware_versions(struct ivtv *itv);
+void ivtv_halt_firmware(struct ivtv *itv);
+void ivtv_init_mpeg_decoder(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
new file mode 100644
index 000000000000..bc8f8ca2961f
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -0,0 +1,307 @@
+/*
+ gpio functions.
+ Merging GPIO support into driver:
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include <media/tuner.h>
+
+/*
+ * GPIO assignment of Yuan MPG600/MPG160
+ *
+ * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0
+ * OUTPUT IN1 IN0 AM3 AM2 AM1 AM0
+ * INPUT DM1 DM0
+ *
+ * IN* : Input selection
+ * IN1 IN0
+ * 1 1 N/A
+ * 1 0 Line
+ * 0 1 N/A
+ * 0 0 Tuner
+ *
+ * AM* : Audio Mode
+ * AM3 0: Normal 1: Mixed(Sub+Main channel)
+ * AM2 0: Subchannel 1: Main channel
+ * AM1 0: Stereo 1: Mono
+ * AM0 0: Normal 1: Mute
+ *
+ * DM* : Detected tuner audio Mode
+ * DM1 0: Stereo 1: Mono
+ * DM0 0: Multiplex 1: Normal
+ *
+ * GPIO Initial Settings
+ * MPG600 MPG160
+ * DIR 0x3080 0x7080
+ * OUTPUT 0x000C 0x400C
+ *
+ * Special thanks to Makoto Iguchi <iguchi@tahoo.org> and Mr. Anonymous
+ * for analyzing GPIO of MPG160.
+ *
+ *****************************************************************************
+ *
+ * GPIO assignment of Avermedia M179 (per information direct from AVerMedia)
+ *
+ * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0
+ * OUTPUT IN0 AM0 IN1 AM1 AM2 IN2 BR0 BR1
+ * INPUT
+ *
+ * IN* : Input selection
+ * IN0 IN1 IN2
+ * * 1 * Mute
+ * 0 0 0 Line-In
+ * 1 0 0 TV Tuner Audio
+ * 0 0 1 FM Audio
+ * 1 0 1 Mute
+ *
+ * AM* : Audio Mode
+ * AM0 AM1 AM2
+ * 0 0 0 TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP
+ * 0 0 1 TV Tuner Audio: L_OUT=R_OUT=SAP (SAP)
+ * 0 1 0 TV Tuner Audio: L_OUT=L, R_OUT=R (stereo)
+ * 0 1 1 TV Tuner Audio: mute
+ * 1 * * TV Tuner Audio: L_OUT=R_OUT=(L+R)/2 (mono)
+ *
+ * BR* : Audio Sample Rate (BR stands for bitrate for some reason)
+ * BR0 BR1
+ * 0 0 32 kHz
+ * 0 1 44.1 kHz
+ * 1 0 48 kHz
+ *
+ * DM* : Detected tuner audio Mode
+ * Unknown currently
+ *
+ * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at
+ * AVerMedia for providing the GPIO information used to add support
+ * for the M179 cards.
+ */
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define IVTV_REG_GPIO_IN 0x9008
+#define IVTV_REG_GPIO_OUT 0x900c
+#define IVTV_REG_GPIO_DIR 0x9020
+
+void ivtv_reset_ir_gpio(struct ivtv *itv)
+{
+ int curdir, curout;
+
+ if (itv->card->type != IVTV_CARD_PVR_150)
+ return;
+ IVTV_DEBUG_INFO("Resetting PVR150 IR\n");
+ curout = read_reg(IVTV_REG_GPIO_OUT);
+ curdir = read_reg(IVTV_REG_GPIO_DIR);
+ curdir |= 0x80;
+ write_reg(curdir, IVTV_REG_GPIO_DIR);
+ curout = (curout & ~0xF) | 1;
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ /* We could use something else for smaller time */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+ curout |= 2;
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ curdir &= ~0x80;
+ write_reg(curdir, IVTV_REG_GPIO_DIR);
+}
+
+#ifdef HAVE_XC3028
+int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr)
+{
+ int curdir, curout;
+ struct ivtv *itv = (struct ivtv *) priv;
+
+ if (itv->card->type != IVTV_CARD_PG600V2 || itv->options.tuner != TUNER_XCEIVE_XC3028)
+ return -EINVAL;
+ IVTV_INFO("Resetting tuner.\n");
+ curout = read_reg(IVTV_REG_GPIO_OUT);
+ curdir = read_reg(IVTV_REG_GPIO_DIR);
+ curdir |= (1 << 12); /* GPIO bit 12 */
+
+ curout &= ~(1 << 12);
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+
+ curout |= (1 << 12);
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+
+ return 0;
+}
+#endif
+
+void ivtv_gpio_init(struct ivtv *itv)
+{
+ if (itv->card->gpio_init.direction == 0)
+ return;
+
+ IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
+
+ /* init output data then direction */
+ write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT);
+ write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR);
+}
+
+static struct v4l2_queryctrl gpio_ctrl_mute = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ .flags = 0,
+};
+
+int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg)
+{
+ struct v4l2_tuner *tuner = arg;
+ struct v4l2_control *ctrl = arg;
+ struct v4l2_routing *route = arg;
+ u16 mask, data;
+
+ switch (command) {
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ mask = itv->card->gpio_audio_freq.mask;
+ switch (*(u32 *)arg) {
+ case 32000:
+ data = itv->card->gpio_audio_freq.f32000;
+ break;
+ case 44100:
+ data = itv->card->gpio_audio_freq.f44100;
+ break;
+ case 48000:
+ default:
+ data = itv->card->gpio_audio_freq.f48000;
+ break;
+ }
+ break;
+
+ case VIDIOC_G_TUNER:
+ mask = itv->card->gpio_audio_detect.mask;
+ if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
+ tuner->rxsubchans = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ else
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+
+ case VIDIOC_S_TUNER:
+ mask = itv->card->gpio_audio_mode.mask;
+ switch (tuner->audmode) {
+ case V4L2_TUNER_MODE_LANG1:
+ data = itv->card->gpio_audio_mode.lang1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ data = itv->card->gpio_audio_mode.lang2;
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ data = itv->card->gpio_audio_mode.mono;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ default:
+ data = itv->card->gpio_audio_mode.stereo;
+ break;
+ }
+ break;
+
+ case AUDC_SET_RADIO:
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.radio;
+ break;
+
+ case VIDIOC_S_STD:
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.tuner;
+ break;
+
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ if (route->input > 2)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_input.mask;
+ switch (route->input) {
+ case 0:
+ data = itv->card->gpio_audio_input.tuner;
+ break;
+ case 1:
+ data = itv->card->gpio_audio_input.linein;
+ break;
+ case 2:
+ default:
+ data = itv->card->gpio_audio_input.radio;
+ break;
+ }
+ break;
+
+ case VIDIOC_G_CTRL:
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_mute.mask;
+ data = itv->card->gpio_audio_mute.mute;
+ ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
+ return 0;
+
+ case VIDIOC_S_CTRL:
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_mute.mask;
+ data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
+ break;
+
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+
+ if (qc->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ *qc = gpio_ctrl_mute;
+ return 0;
+ }
+
+ case VIDIOC_LOG_STATUS:
+ IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
+ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
+ read_reg(IVTV_REG_GPIO_IN));
+ return 0;
+
+ case VIDIOC_INT_S_VIDEO_ROUTING:
+ if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
+ return -EINVAL;
+ mask = itv->card->gpio_video_input.mask;
+ if (route->input == 0)
+ data = itv->card->gpio_video_input.tuner;
+ else if (route->input == 1)
+ data = itv->card->gpio_video_input.composite;
+ else
+ data = itv->card->gpio_video_input.svideo;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h
new file mode 100644
index 000000000000..c301d2a39346
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-gpio.h
@@ -0,0 +1,25 @@
+/*
+ gpio functions.
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+/* GPIO stuff */
+void ivtv_gpio_init(struct ivtv *itv);
+void ivtv_reset_ir_gpio(struct ivtv *itv);
+int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr);
+int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
new file mode 100644
index 000000000000..50624c6a62a5
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -0,0 +1,748 @@
+/*
+ I2C functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+/*
+ This file includes an i2c implementation that was reverse engineered
+ from the Hauppauge windows driver. Older ivtv versions used i2c-algo-bit,
+ which whilst fine under most circumstances, had trouble with the Zilog
+ CPU on the PVR-150 which handles IR functions (occasional inability to
+ communicate with the chip until it was reset) and also with the i2c
+ bus being completely unreachable when multiple PVR cards were present.
+
+ The implementation is very similar to i2c-algo-bit, but there are enough
+ subtle differences that the two are hard to merge. The general strategy
+ employed by i2c-algo-bit is to use udelay() to implement the timing
+ when putting out bits on the scl/sda lines. The general strategy taken
+ here is to poll the lines for state changes (see ivtv_waitscl and
+ ivtv_waitsda). In addition there are small delays at various locations
+ which poll the SCL line 5 times (ivtv_scldelay). I would guess that
+ since this is memory mapped I/O that the length of those delays is tied
+ to the PCI bus clock. There is some extra code to do with recovery
+ and retries. Since it is not known what causes the actual i2c problems
+ in the first place, the only goal if one was to attempt to use
+ i2c-algo-bit would be to try to make it follow the same code path.
+ This would be a lot of work, and I'm also not convinced that it would
+ provide a generic benefit to i2c-algo-bit. Therefore consider this
+ an engineering solution -- not pretty, but it works.
+
+ Some more general comments about what we are doing:
+
+ The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA)
+ lines. To communicate on the bus (as a master, we don't act as a slave),
+ we first initiate a start condition (ivtv_start). We then write the
+ address of the device that we want to communicate with, along with a flag
+ that indicates whether this is a read or a write. The slave then issues
+ an ACK signal (ivtv_ack), which tells us that it is ready for reading /
+ writing. We then proceed with reading or writing (ivtv_read/ivtv_write),
+ and finally issue a stop condition (ivtv_stop) to make the bus available
+ to other masters.
+
+ There is an additional form of transaction where a write may be
+ immediately followed by a read. In this case, there is no intervening
+ stop condition. (Only the msp3400 chip uses this method of data transfer).
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include "ivtv-i2c.h"
+
+#include <media/ir-kbd-i2c.h>
+
+/* i2c implementation for cx23415/6 chip, ivtv project.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ */
+/* i2c stuff */
+#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000
+#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004
+#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008
+#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c
+
+#ifndef I2C_ADAP_CLASS_TV_ANALOG
+#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
+#endif /* I2C_ADAP_CLASS_TV_ANALOG */
+
+#define IVTV_CS53L32A_I2C_ADDR 0x11
+#define IVTV_CX25840_I2C_ADDR 0x44
+#define IVTV_SAA7115_I2C_ADDR 0x21
+#define IVTV_SAA7127_I2C_ADDR 0x44
+#define IVTV_SAA717x_I2C_ADDR 0x21
+#define IVTV_MSP3400_I2C_ADDR 0x40
+#define IVTV_HAUPPAUGE_I2C_ADDR 0x50
+#define IVTV_WM8739_I2C_ADDR 0x1a
+#define IVTV_WM8775_I2C_ADDR 0x1b
+#define IVTV_TEA5767_I2C_ADDR 0x60
+#define IVTV_UPD64031A_I2C_ADDR 0x12
+#define IVTV_UPD64083_I2C_ADDR 0x5c
+#define IVTV_TDA985X_I2C_ADDR 0x5b
+
+/* This array should match the IVTV_HW_ defines */
+static const u8 hw_driverids[] = {
+ I2C_DRIVERID_CX25840,
+ I2C_DRIVERID_SAA711X,
+ I2C_DRIVERID_SAA7127,
+ I2C_DRIVERID_MSP3400,
+ I2C_DRIVERID_TUNER,
+ I2C_DRIVERID_WM8775,
+ I2C_DRIVERID_CS53L32A,
+ I2C_DRIVERID_TVEEPROM,
+ I2C_DRIVERID_SAA711X,
+ I2C_DRIVERID_TVAUDIO,
+ I2C_DRIVERID_UPD64031A,
+ I2C_DRIVERID_UPD64083,
+ I2C_DRIVERID_SAA717X,
+ I2C_DRIVERID_WM8739,
+ 0 /* IVTV_HW_GPIO dummy driver ID */
+};
+
+/* This array should match the IVTV_HW_ defines */
+static const char * const hw_drivernames[] = {
+ "cx2584x",
+ "saa7115",
+ "saa7127",
+ "msp3400",
+ "tuner",
+ "wm8775",
+ "cs53l32a",
+ "tveeprom",
+ "saa7114",
+ "tvaudio",
+ "upd64031a",
+ "upd64083",
+ "saa717x",
+ "wm8739",
+ "gpio",
+};
+
+static int attach_inform(struct i2c_client *client)
+{
+ struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+ int i;
+
+ IVTV_DEBUG_I2C("i2c client attach\n");
+ for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+ if (itv->i2c_clients[i] == NULL) {
+ itv->i2c_clients[i] = client;
+ break;
+ }
+ }
+ if (i == I2C_CLIENTS_MAX) {
+ IVTV_ERR("insufficient room for new I2C client!\n");
+ }
+ return 0;
+}
+
+static int detach_inform(struct i2c_client *client)
+{
+ int i;
+ struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+
+ IVTV_DEBUG_I2C("i2c client detach\n");
+ for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+ if (itv->i2c_clients[i] == client) {
+ itv->i2c_clients[i] = NULL;
+ break;
+ }
+ }
+ IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n",
+ client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
+
+ return 0;
+}
+
+/* Set the serial clock line to the desired state */
+static void ivtv_setscl(struct ivtv *itv, int state)
+{
+ /* write them out */
+ /* write bits are inverted */
+ write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+/* Set the serial data line to the desired state */
+static void ivtv_setsda(struct ivtv *itv, int state)
+{
+ /* write them out */
+ /* write bits are inverted */
+ write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+/* Read the serial clock line */
+static int ivtv_getscl(struct ivtv *itv)
+{
+ return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+/* Read the serial data line */
+static int ivtv_getsda(struct ivtv *itv)
+{
+ return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* Implement a short delay by polling the serial clock line */
+static void ivtv_scldelay(struct ivtv *itv)
+{
+ int i;
+
+ for (i = 0; i < 5; ++i)
+ ivtv_getscl(itv);
+}
+
+/* Wait for the serial clock line to become set to a specific value */
+static int ivtv_waitscl(struct ivtv *itv, int val)
+{
+ int i;
+
+ ivtv_scldelay(itv);
+ for (i = 0; i < 1000; ++i) {
+ if (ivtv_getscl(itv) == val)
+ return 1;
+ }
+ return 0;
+}
+
+/* Wait for the serial data line to become set to a specific value */
+static int ivtv_waitsda(struct ivtv *itv, int val)
+{
+ int i;
+
+ ivtv_scldelay(itv);
+ for (i = 0; i < 1000; ++i) {
+ if (ivtv_getsda(itv) == val)
+ return 1;
+ }
+ return 0;
+}
+
+/* Wait for the slave to issue an ACK */
+static int ivtv_ack(struct ivtv *itv)
+{
+ int ret = 0;
+
+ if (ivtv_getscl(itv) == 1) {
+ IVTV_DEBUG_I2C("SCL was high starting an ack\n");
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n");
+ return -EREMOTEIO;
+ }
+ }
+ ivtv_setsda(itv, 1);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitsda(itv, 0)) {
+ IVTV_DEBUG_I2C("Slave did not ack\n");
+ ret = -EREMOTEIO;
+ }
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n");
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* Write a single byte to the i2c bus and wait for the slave to ACK */
+static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte)
+{
+ int i, bit;
+
+ IVTV_DEBUG_I2C("write %x\n",byte);
+ for (i = 0; i < 8; ++i, byte<<=1) {
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("Error setting SCL low\n");
+ return -EREMOTEIO;
+ }
+ bit = (byte>>7)&1;
+ ivtv_setsda(itv, bit);
+ if (!ivtv_waitsda(itv, bit)) {
+ IVTV_DEBUG_I2C("Error setting SDA\n");
+ return -EREMOTEIO;
+ }
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitscl(itv, 1)) {
+ IVTV_DEBUG_I2C("Slave not ready for bit\n");
+ return -EREMOTEIO;
+ }
+ }
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("Error setting SCL low\n");
+ return -EREMOTEIO;
+ }
+ return ivtv_ack(itv);
+}
+
+/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the
+ final byte) */
+static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack)
+{
+ int i;
+
+ *byte = 0;
+
+ ivtv_setsda(itv, 1);
+ ivtv_scldelay(itv);
+ for (i = 0; i < 8; ++i) {
+ ivtv_setscl(itv, 0);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitscl(itv, 1)) {
+ IVTV_DEBUG_I2C("Error setting SCL high\n");
+ return -EREMOTEIO;
+ }
+ *byte = ((*byte)<<1)|ivtv_getsda(itv);
+ }
+ ivtv_setscl(itv, 0);
+ ivtv_scldelay(itv);
+ ivtv_setsda(itv, nack);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 0);
+ ivtv_scldelay(itv);
+ IVTV_DEBUG_I2C("read %x\n",*byte);
+ return 0;
+}
+
+/* Issue a start condition on the i2c bus to alert slaves to prepare for
+ an address write */
+static int ivtv_start(struct ivtv *itv)
+{
+ int sda;
+
+ sda = ivtv_getsda(itv);
+ if (sda != 1) {
+ IVTV_DEBUG_I2C("SDA was low at start\n");
+ ivtv_setsda(itv, 1);
+ if (!ivtv_waitsda(itv, 1)) {
+ IVTV_DEBUG_I2C("SDA stuck low\n");
+ return -EREMOTEIO;
+ }
+ }
+ if (ivtv_getscl(itv) != 1) {
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitscl(itv, 1)) {
+ IVTV_DEBUG_I2C("SCL stuck low at start\n");
+ return -EREMOTEIO;
+ }
+ }
+ ivtv_setsda(itv, 0);
+ ivtv_scldelay(itv);
+ return 0;
+}
+
+/* Issue a stop condition on the i2c bus to release it */
+static int ivtv_stop(struct ivtv *itv)
+{
+ int i;
+
+ if (ivtv_getscl(itv) != 0) {
+ IVTV_DEBUG_I2C("SCL not low when stopping\n");
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("SCL could not be set low\n");
+ }
+ }
+ ivtv_setsda(itv, 0);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitscl(itv, 1)) {
+ IVTV_DEBUG_I2C("SCL could not be set high\n");
+ return -EREMOTEIO;
+ }
+ ivtv_scldelay(itv);
+ ivtv_setsda(itv, 1);
+ if (!ivtv_waitsda(itv, 1)) {
+ IVTV_DEBUG_I2C("resetting I2C\n");
+ for (i = 0; i < 16; ++i) {
+ ivtv_setscl(itv, 0);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ ivtv_scldelay(itv);
+ ivtv_setsda(itv, 1);
+ }
+ ivtv_waitsda(itv, 1);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+/* Write a message to the given i2c slave. do_stop may be 0 to prevent
+ issuing the i2c stop condition (when following with a read) */
+static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop)
+{
+ int retry, ret = -EREMOTEIO;
+ u32 i;
+
+ for (retry = 0; ret != 0 && retry < 8; ++retry) {
+ ret = ivtv_start(itv);
+
+ if (ret == 0) {
+ ret = ivtv_sendbyte(itv, addr<<1);
+ for (i = 0; ret == 0 && i < len; ++i)
+ ret = ivtv_sendbyte(itv, data[i]);
+ }
+ if (ret != 0 || do_stop) {
+ ivtv_stop(itv);
+ }
+ }
+ if (ret)
+ IVTV_DEBUG_I2C("i2c write to %x failed\n", addr);
+ return ret;
+}
+
+/* Read data from the given i2c slave. A stop condition is always issued. */
+static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len)
+{
+ int retry, ret = -EREMOTEIO;
+ u32 i;
+
+ for (retry = 0; ret != 0 && retry < 8; ++retry) {
+ ret = ivtv_start(itv);
+ if (ret == 0)
+ ret = ivtv_sendbyte(itv, (addr << 1) | 1);
+ for (i = 0; ret == 0 && i < len; ++i) {
+ ret = ivtv_readbyte(itv, &data[i], i == len - 1);
+ }
+ ivtv_stop(itv);
+ }
+ if (ret)
+ IVTV_DEBUG_I2C("i2c read from %x failed\n", addr);
+ return ret;
+}
+
+/* Kernel i2c transfer implementation. Takes a number of messages to be read
+ or written. If a read follows a write, this will occur without an
+ intervening stop condition */
+static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+ struct ivtv *itv = i2c_get_adapdata(i2c_adap);
+ int retval;
+ int i;
+
+ mutex_lock(&itv->i2c_bus_lock);
+ for (i = retval = 0; retval == 0 && i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD)
+ retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len);
+ else {
+ /* if followed by a read, don't stop */
+ int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD);
+
+ retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop);
+ }
+ }
+ mutex_unlock(&itv->i2c_bus_lock);
+ return retval ? retval : num;
+}
+
+/* Kernel i2c capabilities */
+static u32 ivtv_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm ivtv_algo = {
+ .master_xfer = ivtv_xfer,
+ .functionality = ivtv_functionality,
+};
+
+/* template for our-bit banger */
+static struct i2c_adapter ivtv_i2c_adap_hw_template = {
+ .name = "ivtv i2c driver",
+ .id = I2C_HW_B_CX2341X,
+ .algo = &ivtv_algo,
+ .algo_data = NULL, /* filled from template */
+ .client_register = attach_inform,
+ .client_unregister = detach_inform,
+ .owner = THIS_MODULE,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+ .class = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+};
+
+static void ivtv_setscl_old(void *data, int state)
+{
+ struct ivtv *itv = (struct ivtv *)data;
+
+ if (state)
+ itv->i2c_state |= 0x01;
+ else
+ itv->i2c_state &= ~0x01;
+
+ /* write them out */
+ /* write bits are inverted */
+ write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+static void ivtv_setsda_old(void *data, int state)
+{
+ struct ivtv *itv = (struct ivtv *)data;
+
+ if (state)
+ itv->i2c_state |= 0x01;
+ else
+ itv->i2c_state &= ~0x01;
+
+ /* write them out */
+ /* write bits are inverted */
+ write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+static int ivtv_getscl_old(void *data)
+{
+ struct ivtv *itv = (struct ivtv *)data;
+
+ return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+static int ivtv_getsda_old(void *data)
+{
+ struct ivtv *itv = (struct ivtv *)data;
+
+ return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter ivtv_i2c_adap_template = {
+ .name = "ivtv i2c driver",
+ .id = I2C_HW_B_CX2341X, /* algo-bit is OR'd with this */
+ .algo = NULL, /* set by i2c-algo-bit */
+ .algo_data = NULL, /* filled from template */
+ .client_register = attach_inform,
+ .client_unregister = detach_inform,
+ .owner = THIS_MODULE,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+ .class = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+};
+
+static struct i2c_algo_bit_data ivtv_i2c_algo_template = {
+ NULL, /* ?? */
+ ivtv_setsda_old, /* setsda function */
+ ivtv_setscl_old, /* " */
+ ivtv_getsda_old, /* " */
+ ivtv_getscl_old, /* " */
+ 10, /* udelay */
+ 200 /* timeout */
+};
+
+static struct i2c_client ivtv_i2c_client_template = {
+ .name = "ivtv internal",
+};
+
+int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg)
+{
+ struct i2c_client *client;
+ int retval;
+ int i;
+
+ IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
+ for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+ client = itv->i2c_clients[i];
+ if (client == NULL) {
+ continue;
+ }
+ if (client->driver->command == NULL) {
+ continue;
+ }
+ if (addr == client->addr) {
+ retval = client->driver->command(client, cmd, arg);
+ return retval;
+ }
+ }
+ if (cmd != VIDIOC_G_CHIP_IDENT)
+ IVTV_ERR("i2c addr 0x%02x not found for command 0x%x!\n", addr, cmd);
+ return -ENODEV;
+}
+
+/* Find the i2c device based on the driver ID and return
+ its i2c address or -ENODEV if no matching device was found. */
+static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id)
+{
+ struct i2c_client *client;
+ int retval = -ENODEV;
+ int i;
+
+ for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+ client = itv->i2c_clients[i];
+ if (client == NULL)
+ continue;
+ if (id == client->driver->id) {
+ retval = client->addr;
+ break;
+ }
+ }
+ return retval;
+}
+
+/* Find the i2c device name matching the DRIVERID */
+static const char *ivtv_i2c_id_name(u32 id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+ if (hw_driverids[i] == id)
+ return hw_drivernames[i];
+ return "unknown device";
+}
+
+/* Find the i2c device name matching the IVTV_HW_ flag */
+static const char *ivtv_i2c_hw_name(u32 hw)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+ if (1 << i == hw)
+ return hw_drivernames[i];
+ return "unknown device";
+}
+
+/* Find the i2c device matching the IVTV_HW_ flag and return
+ its i2c address or -ENODEV if no matching device was found. */
+int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+ if (1 << i == hw)
+ return ivtv_i2c_id_addr(itv, hw_driverids[i]);
+ return -ENODEV;
+}
+
+/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing.
+ If hw == IVTV_HW_GPIO then call the gpio handler. */
+int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg)
+{
+ int addr;
+
+ if (hw == IVTV_HW_GPIO)
+ return ivtv_gpio(itv, cmd, arg);
+ if (hw == 0)
+ return 0;
+
+ addr = ivtv_i2c_hw_addr(itv, hw);
+ if (addr < 0) {
+ IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x!\n",
+ hw, ivtv_i2c_hw_name(hw), cmd);
+ return addr;
+ }
+ return ivtv_call_i2c_client(itv, addr, cmd, arg);
+}
+
+/* Calls i2c device based on I2C driver ID. */
+int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg)
+{
+ int addr;
+
+ addr = ivtv_i2c_id_addr(itv, id);
+ if (addr < 0) {
+ if (cmd != VIDIOC_G_CHIP_IDENT)
+ IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x!\n",
+ id, ivtv_i2c_id_name(id), cmd);
+ return addr;
+ }
+ return ivtv_call_i2c_client(itv, addr, cmd, arg);
+}
+
+int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg);
+}
+
+/* broadcast cmd for all I2C clients and for the gpio subsystem */
+void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ if (itv->i2c_adap.algo == NULL) {
+ IVTV_ERR("adapter is not set");
+ return;
+ }
+ i2c_clients_command(&itv->i2c_adap, cmd, arg);
+ if (itv->hw_flags & IVTV_HW_GPIO)
+ ivtv_gpio(itv, cmd, arg);
+}
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_ivtv_i2c(struct ivtv *itv)
+{
+ IVTV_DEBUG_I2C("i2c init\n");
+
+ if (itv->options.newi2c > 0) {
+ memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template,
+ sizeof(struct i2c_adapter));
+ } else {
+ memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template,
+ sizeof(struct i2c_adapter));
+ memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
+ sizeof(struct i2c_algo_bit_data));
+ itv->i2c_algo.data = itv;
+ itv->i2c_adap.algo_data = &itv->i2c_algo;
+ }
+
+ sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
+ itv->num);
+ i2c_set_adapdata(&itv->i2c_adap, itv);
+
+ memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
+ sizeof(struct i2c_client));
+ itv->i2c_client.adapter = &itv->i2c_adap;
+ itv->i2c_adap.dev.parent = &itv->dev->dev;
+
+ IVTV_DEBUG_I2C("setting scl and sda to 1\n");
+ ivtv_setscl(itv, 1);
+ ivtv_setsda(itv, 1);
+
+ if (itv->options.newi2c > 0)
+ return i2c_add_adapter(&itv->i2c_adap);
+ else
+ return i2c_bit_add_bus(&itv->i2c_adap);
+}
+
+void __devexit exit_ivtv_i2c(struct ivtv *itv)
+{
+ IVTV_DEBUG_I2C("i2c exit\n");
+
+ i2c_del_adapter(&itv->i2c_adap);
+}
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h
new file mode 100644
index 000000000000..5d210adb5c52
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-i2c.h
@@ -0,0 +1,36 @@
+/*
+ I2C functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg);
+
+int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw);
+int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg);
+int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg);
+int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
+void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg);
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_ivtv_i2c(struct ivtv *itv);
+void __devexit exit_ivtv_i2c(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
new file mode 100644
index 000000000000..794a6a02f82f
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -0,0 +1,1567 @@
+/*
+ ioctl system call
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-fileops.h"
+#include "ivtv-vbi.h"
+#include "ivtv-audio.h"
+#include "ivtv-video.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-gpio.h"
+#include "ivtv-controls.h"
+#include "ivtv-cards.h"
+#include <media/saa7127.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/dvb/audio.h>
+#include <linux/i2c-id.h>
+
+u16 service2vbi(int type)
+{
+ switch (type) {
+ case V4L2_SLICED_TELETEXT_B:
+ return IVTV_SLICED_TYPE_TELETEXT_B;
+ case V4L2_SLICED_CAPTION_525:
+ return IVTV_SLICED_TYPE_CAPTION_525;
+ case V4L2_SLICED_WSS_625:
+ return IVTV_SLICED_TYPE_WSS_625;
+ case V4L2_SLICED_VPS:
+ return IVTV_SLICED_TYPE_VPS;
+ default:
+ return 0;
+ }
+}
+
+static int valid_service_line(int field, int line, int is_pal)
+{
+ return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+ (!is_pal && line >= 10 && line < 22);
+}
+
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+ int i;
+
+ set = set & valid_set;
+ if (set == 0 || !valid_service_line(field, line, is_pal)) {
+ return 0;
+ }
+ if (!is_pal) {
+ if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+ return V4L2_SLICED_CAPTION_525;
+ }
+ else {
+ if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+ return V4L2_SLICED_VPS;
+ if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+ return V4L2_SLICED_WSS_625;
+ if (line == 23)
+ return 0;
+ }
+ for (i = 0; i < 32; i++) {
+ if ((1 << i) & set)
+ return 1 << i;
+ }
+ return 0;
+}
+
+void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ u16 set = fmt->service_set;
+ int f, l;
+
+ fmt->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+ }
+ }
+}
+
+static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+ set |= fmt->service_lines[f][l];
+ }
+ }
+ return set != 0;
+}
+
+u16 get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ set |= fmt->service_lines[f][l];
+ }
+ }
+ return set;
+}
+
+static const struct {
+ v4l2_std_id std;
+ char *name;
+} enum_stds[] = {
+ { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" },
+ { V4L2_STD_PAL_DK, "PAL-DK" },
+ { V4L2_STD_PAL_I, "PAL-I" },
+ { V4L2_STD_PAL_M, "PAL-M" },
+ { V4L2_STD_PAL_N, "PAL-N" },
+ { V4L2_STD_PAL_Nc, "PAL-Nc" },
+ { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" },
+ { V4L2_STD_SECAM_DK, "SECAM-DK" },
+ { V4L2_STD_SECAM_L, "SECAM-L" },
+ { V4L2_STD_SECAM_LC, "SECAM-L'" },
+ { V4L2_STD_NTSC_M, "NTSC-M" },
+ { V4L2_STD_NTSC_M_JP, "NTSC-J" },
+ { V4L2_STD_NTSC_M_KR, "NTSC-K" },
+};
+
+static const struct v4l2_standard ivtv_std_60hz =
+{
+ .frameperiod = {.numerator = 1001, .denominator = 30000},
+ .framelines = 525,
+};
+
+static const struct v4l2_standard ivtv_std_50hz =
+{
+ .frameperiod = {.numerator = 1, .denominator = 25},
+ .framelines = 625,
+};
+
+void ivtv_set_osd_alpha(struct ivtv *itv)
+{
+ ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,
+ itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state);
+ ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_color_key_state, itv->osd_color_key);
+}
+
+int ivtv_set_speed(struct ivtv *itv, int speed)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+ int single_step = (speed == 1 || speed == -1);
+ DEFINE_WAIT(wait);
+
+ if (speed == 0) speed = 1000;
+
+ /* No change? */
+ if (speed == itv->speed && !single_step)
+ return 0;
+
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+ if (single_step && (speed < 0) == (itv->speed < 0)) {
+ /* Single step video and no need to change direction */
+ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+ itv->speed = speed;
+ return 0;
+ }
+ if (single_step)
+ /* Need to change direction */
+ speed = speed < 0 ? -1000 : 1000;
+
+ data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0;
+ data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
+ data[1] = (speed < 0);
+ data[2] = speed < 0 ? 3 : 7;
+ data[3] = itv->params.video_b_frames;
+ data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
+ data[5] = 0;
+ data[6] = 0;
+
+ if (speed == 1500 || speed == -1500) data[0] |= 1;
+ else if (speed == 2000 || speed == -2000) data[0] |= 2;
+ else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed);
+ else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed);
+
+ /* If not decoding, just change speed setting */
+ if (atomic_read(&itv->decoding) > 0) {
+ int got_sig = 0;
+
+ /* Stop all DMA and decoding activity */
+ ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0);
+
+ /* Wait for any DMA to finish */
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ while (itv->i_flags & IVTV_F_I_DMA) {
+ got_sig = signal_pending(current);
+ if (got_sig)
+ break;
+ got_sig = 0;
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+ if (got_sig)
+ return -EINTR;
+
+ /* Change Speed safely */
+ ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data);
+ IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+ }
+ if (single_step) {
+ speed = (speed < 0) ? -1 : 1;
+ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+ }
+ itv->speed = speed;
+ return 0;
+}
+
+static int ivtv_validate_speed(int cur_speed, int new_speed)
+{
+ int fact = new_speed < 0 ? -1 : 1;
+ int s;
+
+ if (new_speed < 0) new_speed = -new_speed;
+ if (cur_speed < 0) cur_speed = -cur_speed;
+
+ if (cur_speed <= new_speed) {
+ if (new_speed > 1500) return fact * 2000;
+ if (new_speed > 1000) return fact * 1500;
+ }
+ else {
+ if (new_speed >= 2000) return fact * 2000;
+ if (new_speed >= 1500) return fact * 1500;
+ if (new_speed >= 1000) return fact * 1000;
+ }
+ if (new_speed == 0) return 1000;
+ if (new_speed == 1 || new_speed == 1000) return fact * new_speed;
+
+ s = new_speed;
+ new_speed = 1000 / new_speed;
+ if (1000 / cur_speed == new_speed)
+ new_speed += (cur_speed < s) ? -1 : 1;
+ if (new_speed > 60) return 1000 / (fact * 60);
+ return 1000 / (fact * new_speed);
+}
+
+static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
+ struct video_command *vc, int try)
+{
+ struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ switch (vc->cmd) {
+ case VIDEO_CMD_PLAY: {
+ vc->flags = 0;
+ vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed);
+ if (vc->play.speed < 0)
+ vc->play.format = VIDEO_PLAY_FMT_GOP;
+ if (try) break;
+
+ if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
+ return -EBUSY;
+ return ivtv_start_decoding(id, vc->play.speed);
+ }
+
+ case VIDEO_CMD_STOP:
+ vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK;
+ if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY)
+ vc->stop.pts = 0;
+ if (try) break;
+ if (atomic_read(&itv->decoding) == 0)
+ return 0;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+
+ itv->output_mode = OUT_NONE;
+ return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts);
+
+ case VIDEO_CMD_FREEZE:
+ vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK;
+ if (try) break;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+ if (atomic_read(&itv->decoding) > 0) {
+ ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
+ (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0);
+ }
+ break;
+
+ case VIDEO_CMD_CONTINUE:
+ vc->flags = 0;
+ if (try) break;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+ if (atomic_read(&itv->decoding) > 0) {
+ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 0);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ struct v4l2_register *regs = arg;
+ unsigned long flags;
+ volatile u8 __iomem *reg_start;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE)
+ reg_start = itv->reg_mem - IVTV_REG_OFFSET;
+ else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET &&
+ regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE)
+ reg_start = itv->dec_mem - IVTV_DECODER_OFFSET;
+ else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE)
+ reg_start = itv->enc_mem;
+ else
+ return -EINVAL;
+
+ spin_lock_irqsave(&ivtv_cards_lock, flags);
+ if (cmd == VIDIOC_DBG_G_REGISTER) {
+ regs->val = readl(regs->reg + reg_start);
+ } else {
+ writel(regs->val, regs->reg + reg_start);
+ }
+ spin_unlock_irqrestore(&ivtv_cards_lock, flags);
+ return 0;
+}
+
+static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt)
+{
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ fmt->fmt.pix.left = itv->main_rect.left;
+ fmt->fmt.pix.top = itv->main_rect.top;
+ fmt->fmt.pix.width = itv->main_rect.width;
+ fmt->fmt.pix.height = itv->main_rect.height;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ if (itv->output_mode == OUT_UDMA_YUV) {
+ switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
+ case IVTV_YUV_MODE_INTERLACED:
+ fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
+ V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
+ break;
+ case IVTV_YUV_MODE_PROGRESSIVE:
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ break;
+ default:
+ fmt->fmt.pix.field = V4L2_FIELD_ANY;
+ break;
+ }
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+ /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+ fmt->fmt.pix.sizeimage =
+ fmt->fmt.pix.height * fmt->fmt.pix.width +
+ fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+ }
+ else if (itv->output_mode == OUT_YUV ||
+ streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
+ streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+ /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+ fmt->fmt.pix.sizeimage =
+ fmt->fmt.pix.height * fmt->fmt.pix.width +
+ fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+ } else {
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ fmt->fmt.pix.sizeimage = 128 * 1024;
+ }
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ fmt->fmt.pix.left = 0;
+ fmt->fmt.pix.top = 0;
+ fmt->fmt.pix.width = itv->params.width;
+ fmt->fmt.pix.height = itv->params.height;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ if (streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
+ streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+ /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+ fmt->fmt.pix.sizeimage =
+ fmt->fmt.pix.height * fmt->fmt.pix.width +
+ fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+ } else {
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ fmt->fmt.pix.sizeimage = 128 * 1024;
+ }
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ fmt->fmt.win.chromakey = itv->osd_color_key;
+ fmt->fmt.win.global_alpha = itv->osd_global_alpha;
+ break;
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ fmt->fmt.vbi.sampling_rate = 27000000;
+ fmt->fmt.vbi.offset = 248;
+ fmt->fmt.vbi.samples_per_line = itv->vbi.raw_decoder_line_size - 4;
+ fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ fmt->fmt.vbi.start[0] = itv->vbi.start[0];
+ fmt->fmt.vbi.start[1] = itv->vbi.start[1];
+ fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = itv->vbi.count;
+ break;
+
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ {
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+ return -EINVAL;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+ if (itv->is_60hz) {
+ vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ } else {
+ vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
+ }
+ vbifmt->service_set = get_service_set(vbifmt);
+ break;
+ }
+
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ {
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+
+ if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
+ vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
+ V4L2_SLICED_VBI_525;
+ expand_service_set(vbifmt, itv->is_50hz);
+ break;
+ }
+
+ itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);
+ vbifmt->service_set = get_service_set(vbifmt);
+ break;
+ }
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype,
+ struct v4l2_format *fmt, int set_fmt)
+{
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+ u16 set;
+
+ if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ struct v4l2_rect r;
+ int field;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ field = fmt->fmt.pix.field;
+ r.top = fmt->fmt.pix.top;
+ r.left = fmt->fmt.pix.left;
+ r.width = fmt->fmt.pix.width;
+ r.height = fmt->fmt.pix.height;
+ ivtv_get_fmt(itv, streamtype, fmt);
+ if (itv->output_mode != OUT_UDMA_YUV) {
+ /* TODO: would setting the rect also be valid for this mode? */
+ fmt->fmt.pix.top = r.top;
+ fmt->fmt.pix.left = r.left;
+ fmt->fmt.pix.width = r.width;
+ fmt->fmt.pix.height = r.height;
+ }
+ if (itv->output_mode == OUT_UDMA_YUV) {
+ /* TODO: add checks for validity */
+ fmt->fmt.pix.field = field;
+ }
+ if (set_fmt) {
+ if (itv->output_mode == OUT_UDMA_YUV) {
+ switch (field) {
+ case V4L2_FIELD_NONE:
+ itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
+ break;
+ case V4L2_FIELD_ANY:
+ itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ itv->yuv_info.lace_mode =
+ IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ default:
+ itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED;
+ break;
+ }
+ itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+
+ /* Force update of yuv registers */
+ itv->yuv_info.yuv_forced_update = 1;
+ return 0;
+ }
+ if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+ r.width, r.height, r.left, r.top))
+ itv->main_rect = r;
+ else
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) {
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ if (set_fmt) {
+ itv->osd_color_key = fmt->fmt.win.chromakey;
+ itv->osd_global_alpha = fmt->fmt.win.global_alpha;
+ ivtv_set_osd_alpha(itv);
+ }
+ return 0;
+ }
+
+ /* set window size */
+ if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
+
+ if (w > 720) w = 720;
+ else if (w < 1) w = 1;
+ if (h > (itv->is_50hz ? 576 : 480)) h = (itv->is_50hz ? 576 : 480);
+ else if (h < 2) h = 2;
+ ivtv_get_fmt(itv, streamtype, fmt);
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+
+ if (!set_fmt || (itv->params.width == w && itv->params.height == h))
+ return 0;
+ if (atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
+
+ itv->params.width = w;
+ itv->params.height = h;
+ if (w != 720 || h != (itv->is_50hz ? 576 : 480))
+ itv->params.video_temporal_filter = 0;
+ else
+ itv->params.video_temporal_filter = 8;
+ itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ return ivtv_get_fmt(itv, streamtype, fmt);
+ }
+
+ /* set raw VBI format */
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (set_fmt && streamtype == IVTV_ENC_STREAM_TYPE_VBI &&
+ itv->vbi.sliced_in->service_set &&
+ atomic_read(&itv->capturing) > 0) {
+ return -EBUSY;
+ }
+ if (set_fmt) {
+ itv->vbi.sliced_in->service_set = 0;
+ itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+ }
+ return ivtv_get_fmt(itv, streamtype, fmt);
+ }
+
+ /* set sliced VBI output
+ In principle the user could request that only certain
+ VBI types are output and that the others are ignored.
+ I.e., suppress CC in the even fields or only output
+ WSS and no VPS. Currently though there is no choice. */
+ if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
+ return ivtv_get_fmt(itv, streamtype, fmt);
+
+ /* any else but sliced VBI capture is an error */
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+
+ if (streamtype == IVTV_DEC_STREAM_TYPE_VBI)
+ return ivtv_get_fmt(itv, streamtype, fmt);
+
+ /* set sliced VBI capture format */
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+
+ if (vbifmt->service_set)
+ expand_service_set(vbifmt, itv->is_50hz);
+ set = check_service_set(vbifmt, itv->is_50hz);
+ vbifmt->service_set = get_service_set(vbifmt);
+
+ if (!set_fmt)
+ return 0;
+ if (set == 0)
+ return -EINVAL;
+ if (atomic_read(&itv->capturing) > 0 && itv->vbi.sliced_in->service_set == 0) {
+ return -EBUSY;
+ }
+ itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
+ return 0;
+}
+
+static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+ struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+ struct ivtv *itv = id->itv;
+ struct v4l2_register *reg = arg;
+
+ switch (cmd) {
+ /* ioctls to allow direct access to the encoder registers for testing */
+ case VIDIOC_DBG_G_REGISTER:
+ if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return ivtv_itvc(itv, cmd, arg);
+ if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+ return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+
+ case VIDIOC_DBG_S_REGISTER:
+ if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return ivtv_itvc(itv, cmd, arg);
+ if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+ return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+
+ case VIDIOC_G_CHIP_IDENT: {
+ struct v4l2_chip_ident *chip = arg;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
+ if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
+ struct v4l2_chip_ident *chip = arg;
+
+ chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
+ }
+ return 0;
+ }
+ if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+ if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
+ return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+ return -EINVAL;
+ }
+
+ case VIDIOC_INT_S_AUDIO_ROUTING: {
+ struct v4l2_routing *route = arg;
+
+ ivtv_audio_set_route(itv, route);
+ break;
+ }
+
+ case VIDIOC_INT_RESET:
+ ivtv_reset_ir_gpio(itv);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg)
+{
+ struct ivtv_open_id *id = NULL;
+
+ if (filp) id = (struct ivtv_open_id *)filp->private_data;
+
+ switch (cmd) {
+ case VIDIOC_G_PRIORITY:
+ {
+ enum v4l2_priority *p = arg;
+
+ *p = v4l2_prio_max(&itv->prio);
+ break;
+ }
+
+ case VIDIOC_S_PRIORITY:
+ {
+ enum v4l2_priority *prio = arg;
+
+ return v4l2_prio_change(&itv->prio, &id->prio, *prio);
+ }
+
+ case VIDIOC_QUERYCAP:{
+ struct v4l2_capability *vcap = arg;
+
+ memset(vcap, 0, sizeof(*vcap));
+ strcpy(vcap->driver, IVTV_DRIVER_NAME); /* driver name */
+ strcpy(vcap->card, itv->card_name); /* card type */
+ strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */
+ vcap->version = IVTV_DRIVER_VERSION; /* version */
+ vcap->capabilities = itv->v4l2_cap; /* capabilities */
+
+ /* reserved.. must set to 0! */
+ vcap->reserved[0] = vcap->reserved[1] =
+ vcap->reserved[2] = vcap->reserved[3] = 0;
+ break;
+ }
+
+ case VIDIOC_ENUMAUDIO:{
+ struct v4l2_audio *vin = arg;
+
+ return ivtv_get_audio_input(itv, vin->index, vin);
+ }
+
+ case VIDIOC_G_AUDIO:{
+ struct v4l2_audio *vin = arg;
+
+ vin->index = itv->audio_input;
+ return ivtv_get_audio_input(itv, vin->index, vin);
+ }
+
+ case VIDIOC_S_AUDIO:{
+ struct v4l2_audio *vout = arg;
+
+ if (vout->index >= itv->nof_audio_inputs)
+ return -EINVAL;
+ itv->audio_input = vout->index;
+ ivtv_audio_set_io(itv);
+ break;
+ }
+
+ case VIDIOC_ENUMAUDOUT:{
+ struct v4l2_audioout *vin = arg;
+
+ /* set it to defaults from our table */
+ return ivtv_get_audio_output(itv, vin->index, vin);
+ }
+
+ case VIDIOC_G_AUDOUT:{
+ struct v4l2_audioout *vin = arg;
+
+ vin->index = 0;
+ return ivtv_get_audio_output(itv, vin->index, vin);
+ }
+
+ case VIDIOC_S_AUDOUT:{
+ struct v4l2_audioout *vout = arg;
+
+ return ivtv_get_audio_output(itv, vout->index, vout);
+ }
+
+ case VIDIOC_ENUMINPUT:{
+ struct v4l2_input *vin = arg;
+
+ /* set it to defaults from our table */
+ return ivtv_get_input(itv, vin->index, vin);
+ }
+
+ case VIDIOC_ENUMOUTPUT:{
+ struct v4l2_output *vout = arg;
+
+ return ivtv_get_output(itv, vout->index, vout);
+ }
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT: {
+ struct v4l2_format *fmt = arg;
+
+ return ivtv_try_or_set_fmt(itv, id->type, fmt, cmd == VIDIOC_S_FMT);
+ }
+
+ case VIDIOC_G_FMT: {
+ struct v4l2_format *fmt = arg;
+ int type = fmt->type;
+
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->type = type;
+ return ivtv_get_fmt(itv, id->type, fmt);
+ }
+
+ case VIDIOC_S_CROP: {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return itv->video_dec_func(itv, VIDIOC_S_CROP, arg);
+ }
+
+ case VIDIOC_G_CROP: {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return itv->video_dec_func(itv, VIDIOC_G_CROP, arg);
+ }
+
+ case VIDIOC_ENUM_FMT: {
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
+ { 0, 0, 0, 0 }
+ },
+ { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
+ "MPEG", V4L2_PIX_FMT_MPEG,
+ { 0, 0, 0, 0 }
+ }
+ };
+ struct v4l2_fmtdesc *fmt = arg;
+ enum v4l2_buf_type type = fmt->type;
+
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (fmt->index > 1)
+ return -EINVAL;
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
+ }
+
+ case VIDIOC_G_INPUT:{
+ *(int *)arg = itv->active_input;
+ break;
+ }
+
+ case VIDIOC_S_INPUT:{
+ int inp = *(int *)arg;
+
+ if (inp < 0 || inp >= itv->nof_inputs)
+ return -EINVAL;
+
+ if (inp == itv->active_input) {
+ IVTV_DEBUG_INFO("Input unchanged\n");
+ break;
+ }
+ IVTV_DEBUG_INFO("Changing input from %d to %d\n",
+ itv->active_input, inp);
+
+ itv->active_input = inp;
+ /* Set the audio input to whatever is appropriate for the
+ input type. */
+ itv->audio_input = itv->card->video_inputs[inp].audio_index;
+
+ /* prevent others from messing with the streams until
+ we're finished changing inputs. */
+ ivtv_mute(itv);
+ ivtv_video_set_io(itv);
+ ivtv_audio_set_io(itv);
+ ivtv_unmute(itv);
+ break;
+ }
+
+ case VIDIOC_G_OUTPUT:{
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ *(int *)arg = itv->active_output;
+ break;
+ }
+
+ case VIDIOC_S_OUTPUT:{
+ int outp = *(int *)arg;
+ struct v4l2_routing route;
+
+ if (outp >= itv->card->nof_outputs)
+ return -EINVAL;
+
+ if (outp == itv->active_output) {
+ IVTV_DEBUG_INFO("Output unchanged\n");
+ break;
+ }
+ IVTV_DEBUG_INFO("Changing output from %d to %d\n",
+ itv->active_output, outp);
+
+ itv->active_output = outp;
+ route.input = SAA7127_INPUT_TYPE_NORMAL;
+ route.output = itv->card->video_outputs[outp].video_output;
+ ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ break;
+ }
+
+ case VIDIOC_G_FREQUENCY:{
+ struct v4l2_frequency *vf = arg;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+ ivtv_call_i2c_clients(itv, cmd, arg);
+ break;
+ }
+
+ case VIDIOC_S_FREQUENCY:{
+ struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
+
+ if (vf.tuner != 0)
+ return -EINVAL;
+
+ ivtv_mute(itv);
+ IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
+ ivtv_call_i2c_clients(itv, cmd, &vf);
+ ivtv_unmute(itv);
+ break;
+ }
+
+ case VIDIOC_ENUMSTD:{
+ struct v4l2_standard *vs = arg;
+ int idx = vs->index;
+
+ if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
+ return -EINVAL;
+
+ *vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
+ ivtv_std_60hz : ivtv_std_50hz;
+ vs->index = idx;
+ vs->id = enum_stds[idx].std;
+ strcpy(vs->name, enum_stds[idx].name);
+ break;
+ }
+
+ case VIDIOC_G_STD:{
+ *(v4l2_std_id *) arg = itv->std;
+ break;
+ }
+
+ case VIDIOC_S_STD: {
+ v4l2_std_id std = *(v4l2_std_id *) arg;
+
+ if ((std & V4L2_STD_ALL) == 0)
+ return -EINVAL;
+
+ if (std == itv->std)
+ break;
+
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
+ atomic_read(&itv->capturing) > 0 ||
+ atomic_read(&itv->decoding) > 0) {
+ /* Switching standard would turn off the radio or mess
+ with already running streams, prevent that by
+ returning EBUSY. */
+ return -EBUSY;
+ }
+
+ itv->std = std;
+ itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
+ itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
+ itv->params.width = 720;
+ itv->params.height = itv->is_50hz ? 576 : 480;
+ itv->vbi.count = itv->is_50hz ? 18 : 12;
+ itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
+ itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
+ if (itv->hw_flags & IVTV_HW_CX25840) {
+ itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
+ }
+ IVTV_DEBUG_INFO("Switching standard to %llx.\n", itv->std);
+
+ /* Tuner */
+ ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ /* set display standard */
+ itv->std_out = std;
+ itv->is_out_60hz = itv->is_60hz;
+ itv->is_out_50hz = itv->is_50hz;
+ ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out);
+ ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
+ itv->main_rect.left = itv->main_rect.top = 0;
+ itv->main_rect.width = 720;
+ itv->main_rect.height = itv->params.height;
+ ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+ 720, itv->main_rect.height, 0, 0);
+ }
+ break;
+ }
+
+ case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */
+ struct v4l2_tuner *vt = arg;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt);
+ break;
+ }
+
+ case VIDIOC_G_TUNER: {
+ struct v4l2_tuner *vt = arg;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ memset(vt, 0, sizeof(*vt));
+ ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
+
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+ strcpy(vt->name, "ivtv Radio Tuner");
+ vt->type = V4L2_TUNER_RADIO;
+ } else {
+ strcpy(vt->name, "ivtv TV Tuner");
+ vt->type = V4L2_TUNER_ANALOG_TV;
+ }
+ break;
+ }
+
+ case VIDIOC_G_SLICED_VBI_CAP: {
+ struct v4l2_sliced_vbi_cap *cap = arg;
+ int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+ int f, l;
+ enum v4l2_buf_type type = cap->type;
+
+ memset(cap, 0, sizeof(*cap));
+ cap->type = type;
+ if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ if (valid_service_line(f, l, itv->is_50hz)) {
+ cap->service_lines[f][l] = set;
+ }
+ }
+ }
+ return 0;
+ }
+ if (type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+ return -EINVAL;
+ if (itv->is_60hz) {
+ cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ } else {
+ cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ cap->service_lines[0][16] = V4L2_SLICED_VPS;
+ }
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ case VIDIOC_G_ENC_INDEX: {
+ struct v4l2_enc_idx *idx = arg;
+ int i;
+
+ idx->entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
+ IVTV_MAX_PGM_INDEX;
+ if (idx->entries > V4L2_ENC_IDX_ENTRIES)
+ idx->entries = V4L2_ENC_IDX_ENTRIES;
+ for (i = 0; i < idx->entries; i++) {
+ idx->entry[i] = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
+ }
+ itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
+ break;
+ }
+
+ case VIDIOC_ENCODER_CMD:
+ case VIDIOC_TRY_ENCODER_CMD: {
+ struct v4l2_encoder_cmd *enc = arg;
+ int try = cmd == VIDIOC_TRY_ENCODER_CMD;
+
+ memset(&enc->raw, 0, sizeof(enc->raw));
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ enc->flags = 0;
+ if (try)
+ return 0;
+ return ivtv_start_capture(id);
+
+ case V4L2_ENC_CMD_STOP:
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ if (try)
+ return 0;
+ ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+ return 0;
+
+ case V4L2_ENC_CMD_PAUSE:
+ enc->flags = 0;
+ if (try)
+ return 0;
+ if (!atomic_read(&itv->capturing))
+ return -EPERM;
+ if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ return 0;
+ ivtv_mute(itv);
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
+ break;
+
+ case V4L2_ENC_CMD_RESUME:
+ enc->flags = 0;
+ if (try)
+ return 0;
+ if (!atomic_read(&itv->capturing))
+ return -EPERM;
+ if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ return 0;
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+ ivtv_unmute(itv);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VIDIOC_G_FBUF: {
+ struct v4l2_framebuffer *fb = arg;
+
+ memset(fb, 0, sizeof(*fb));
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+ break;
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
+ V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_GLOBAL_ALPHA;
+ fb->fmt.pixelformat = itv->osd_pixelformat;
+ fb->fmt.width = itv->osd_rect.width;
+ fb->fmt.height = itv->osd_rect.height;
+ fb->fmt.left = itv->osd_rect.left;
+ fb->fmt.top = itv->osd_rect.top;
+ fb->base = (void *)itv->osd_video_pbase;
+ if (itv->osd_global_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+ if (itv->osd_local_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ if (itv->osd_color_key_state)
+ fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+ break;
+ }
+
+ case VIDIOC_S_FBUF: {
+ struct v4l2_framebuffer *fb = arg;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+ break;
+ itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+ itv->osd_local_alpha_state = (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0;
+ itv->osd_color_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+ break;
+ }
+
+ case VIDIOC_LOG_STATUS:
+ {
+ int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
+ struct v4l2_input vidin;
+ struct v4l2_audio audin;
+ int i;
+
+ IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num);
+ if (itv->hw_flags & IVTV_HW_TVEEPROM) {
+ struct tveeprom tv;
+
+ ivtv_read_eeprom(itv, &tv);
+ }
+ ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL);
+ ivtv_get_input(itv, itv->active_input, &vidin);
+ ivtv_get_audio_input(itv, itv->audio_input, &audin);
+ IVTV_INFO("Video Input: %s\n", vidin.name);
+ IVTV_INFO("Audio Input: %s\n", audin.name);
+ if (has_output) {
+ struct v4l2_output vidout;
+ struct v4l2_audioout audout;
+ int mode = itv->output_mode;
+ static const char * const output_modes[] = {
+ "None",
+ "MPEG Streaming",
+ "YUV Streaming",
+ "YUV Frames",
+ "Passthrough",
+ };
+
+ ivtv_get_output(itv, itv->active_output, &vidout);
+ ivtv_get_audio_output(itv, 0, &audout);
+ IVTV_INFO("Video Output: %s\n", vidout.name);
+ IVTV_INFO("Audio Output: %s\n", audout.name);
+ if (mode < 0 || mode > OUT_PASSTHROUGH)
+ mode = OUT_NONE;
+ IVTV_INFO("Output Mode: %s\n", output_modes[mode]);
+ }
+ IVTV_INFO("Tuner: %s\n",
+ test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
+ cx2341x_log_status(&itv->params, itv->name);
+ IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ struct ivtv_stream *s = &itv->streams[i];
+
+ if (s->v4l2dev == NULL || s->buffers == 0)
+ continue;
+ IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
+ (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+ (s->buffers * s->buf_size) / 1024, s->buffers);
+ }
+ IVTV_INFO("Read MPEG/VBI: %lld/%lld bytes\n", itv->mpg_data_received, itv->vbi_data_inserted);
+ IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num);
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+ struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+ struct ivtv *itv = id->itv;
+ int nonblocking = filp->f_flags & O_NONBLOCK;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ switch (cmd) {
+ case IVTV_IOC_DMA_FRAME: {
+ struct ivtv_dma_frame *args = arg;
+
+ IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL)
+ return 0;
+ if (ivtv_claim_stream(id, id->type)) {
+ return -EBUSY;
+ }
+ if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) {
+ ivtv_release_stream(s);
+ return -EBUSY;
+ }
+ if (args->y_source == NULL)
+ return 0;
+ return ivtv_yuv_prep_frame(itv, args);
+ }
+
+ case VIDEO_GET_PTS: {
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ u64 *pts = arg;
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
+ if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+ *pts = s->dma_pts;
+ break;
+ }
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+ *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) |
+ (u64)itv->last_dec_timing[1];
+ break;
+ }
+ *pts = 0;
+ if (atomic_read(&itv->decoding)) {
+ if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+ IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+ return -EIO;
+ }
+ memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+ set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+ *pts = (u64) ((u64) data[2] << 32) | (u64) data[1];
+ /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
+ }
+ break;
+ }
+
+ case VIDEO_GET_FRAME_COUNT: {
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ u64 *frame = arg;
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
+ if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+ *frame = 0;
+ break;
+ }
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+ *frame = itv->last_dec_timing[0];
+ break;
+ }
+ *frame = 0;
+ if (atomic_read(&itv->decoding)) {
+ if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+ IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+ return -EIO;
+ }
+ memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+ set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+ *frame = data[0];
+ }
+ break;
+ }
+
+ case VIDEO_PLAY: {
+ struct video_command vc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
+ memset(&vc, 0, sizeof(vc));
+ vc.cmd = VIDEO_CMD_PLAY;
+ return ivtv_video_command(itv, id, &vc, 0);
+ }
+
+ case VIDEO_STOP: {
+ struct video_command vc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
+ memset(&vc, 0, sizeof(vc));
+ vc.cmd = VIDEO_CMD_STOP;
+ vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY;
+ return ivtv_video_command(itv, id, &vc, 0);
+ }
+
+ case VIDEO_FREEZE: {
+ struct video_command vc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
+ memset(&vc, 0, sizeof(vc));
+ vc.cmd = VIDEO_CMD_FREEZE;
+ return ivtv_video_command(itv, id, &vc, 0);
+ }
+
+ case VIDEO_CONTINUE: {
+ struct video_command vc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
+ memset(&vc, 0, sizeof(vc));
+ vc.cmd = VIDEO_CMD_CONTINUE;
+ return ivtv_video_command(itv, id, &vc, 0);
+ }
+
+ case VIDEO_COMMAND:
+ case VIDEO_TRY_COMMAND: {
+ struct video_command *vc = arg;
+ int try = (cmd == VIDEO_TRY_COMMAND);
+
+ if (try)
+ IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND\n");
+ else
+ IVTV_DEBUG_IOCTL("VIDEO_COMMAND\n");
+ return ivtv_video_command(itv, id, vc, try);
+ }
+
+ case VIDEO_GET_EVENT: {
+ struct video_event *ev = arg;
+ DEFINE_WAIT(wait);
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ memset(ev, 0, sizeof(*ev));
+ set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+
+ while (1) {
+ if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+ ev->type = VIDEO_EVENT_DECODER_STOPPED;
+ else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) {
+ ev->type = VIDEO_EVENT_VSYNC;
+ ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ?
+ VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN;
+ if (itv->output_mode == OUT_UDMA_YUV &&
+ (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) ==
+ IVTV_YUV_MODE_PROGRESSIVE) {
+ ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE;
+ }
+ }
+ if (ev->type)
+ return 0;
+ if (nonblocking)
+ return -EAGAIN;
+ /* wait for event */
+ prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE);
+ if ((itv->i_flags & (IVTV_F_I_EV_DEC_STOPPED|IVTV_F_I_EV_VSYNC)) == 0)
+ schedule();
+ finish_wait(&itv->event_waitq, &wait);
+ if (signal_pending(current)) {
+ /* return if a signal was received */
+ IVTV_DEBUG_INFO("User stopped wait for event\n");
+ return -EINTR;
+ }
+ }
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, void *arg)
+{
+ struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+ struct ivtv *itv = id->itv;
+ int ret;
+
+ /* check priority */
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_S_FMT:
+ case VIDIOC_S_CROP:
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_S_AUDOUT:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_S_FBUF:
+ ret = v4l2_prio_check(&itv->prio, &id->prio);
+ if (ret)
+ return ret;
+ }
+
+ switch (cmd) {
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_G_CHIP_IDENT:
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ case VIDIOC_INT_RESET:
+ if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
+ printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
+ v4l_printk_ioctl(cmd);
+ }
+ return ivtv_debug_ioctls(filp, cmd, arg);
+
+ case VIDIOC_G_PRIORITY:
+ case VIDIOC_S_PRIORITY:
+ case VIDIOC_QUERYCAP:
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_ENUMOUTPUT:
+ case VIDIOC_G_OUTPUT:
+ case VIDIOC_S_OUTPUT:
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_ENUMAUDIO:
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_G_AUDIO:
+ case VIDIOC_ENUMAUDOUT:
+ case VIDIOC_S_AUDOUT:
+ case VIDIOC_G_AUDOUT:
+ case VIDIOC_G_SLICED_VBI_CAP:
+ case VIDIOC_LOG_STATUS:
+ case VIDIOC_G_ENC_INDEX:
+ case VIDIOC_ENCODER_CMD:
+ case VIDIOC_TRY_ENCODER_CMD:
+ case VIDIOC_G_FBUF:
+ case VIDIOC_S_FBUF:
+ if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
+ printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
+ v4l_printk_ioctl(cmd);
+ }
+ return ivtv_v4l2_ioctls(itv, filp, cmd, arg);
+
+ case VIDIOC_QUERYMENU:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
+ printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
+ v4l_printk_ioctl(cmd);
+ }
+ return ivtv_control_ioctls(itv, cmd, arg);
+
+ case IVTV_IOC_DMA_FRAME:
+ case VIDEO_GET_PTS:
+ case VIDEO_GET_FRAME_COUNT:
+ case VIDEO_GET_EVENT:
+ case VIDEO_PLAY:
+ case VIDEO_STOP:
+ case VIDEO_FREEZE:
+ case VIDEO_CONTINUE:
+ case VIDEO_COMMAND:
+ case VIDEO_TRY_COMMAND:
+ return ivtv_decoder_ioctls(filp, cmd, arg);
+
+ case 0x00005401: /* Handle isatty() calls */
+ return -EINVAL;
+ default:
+ return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
+ ivtv_v4l2_do_ioctl);
+ }
+ return 0;
+}
+
+int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+ struct ivtv *itv = id->itv;
+
+ /* Filter dvb ioctls that cannot be handled by video_usercopy */
+ switch (cmd) {
+ case VIDEO_SELECT_SOURCE:
+ IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_passthrough_mode(itv, arg == VIDEO_SOURCE_DEMUX);
+
+ case AUDIO_SET_MUTE:
+ IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n");
+ itv->speed_mute_audio = arg;
+ return 0;
+
+ case AUDIO_CHANNEL_SELECT:
+ IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
+ if (arg > AUDIO_STEREO_SWAPPED)
+ return -EINVAL;
+ itv->audio_stereo_mode = arg;
+ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+ return 0;
+
+ case AUDIO_BILINGUAL_CHANNEL_SELECT:
+ IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
+ if (arg > AUDIO_STEREO_SWAPPED)
+ return -EINVAL;
+ itv->audio_bilingual_mode = arg;
+ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+ return 0;
+
+ default:
+ break;
+ }
+ return video_usercopy(inode, filp, cmd, arg, ivtv_v4l2_do_ioctl);
+}
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h
new file mode 100644
index 000000000000..cbccf7a9f65c
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-ioctl.h
@@ -0,0 +1,28 @@
+/*
+ ioctl system call
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+u16 service2vbi(int type);
+void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 get_service_set(struct v4l2_sliced_vbi_format *fmt);
+int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg);
+int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg);
+void ivtv_set_osd_alpha(struct ivtv *itv);
+int ivtv_set_speed(struct ivtv *itv, int speed);
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
new file mode 100644
index 000000000000..c3a047b381b3
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -0,0 +1,838 @@
+/* interrupt handling
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-firmware.h"
+#include "ivtv-fileops.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-vbi.h"
+#include "ivtv-yuv.h"
+
+#define DMA_MAGIC_COOKIE 0x000001fe
+
+#define SLICED_VBI_PIO 1
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s);
+
+static const int ivtv_stream_map[] = {
+ IVTV_ENC_STREAM_TYPE_MPG,
+ IVTV_ENC_STREAM_TYPE_YUV,
+ IVTV_ENC_STREAM_TYPE_PCM,
+ IVTV_ENC_STREAM_TYPE_VBI,
+};
+
+static inline int ivtv_use_pio(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ return s->dma == PCI_DMA_NONE ||
+ (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set);
+}
+
+void ivtv_irq_work_handler(struct work_struct *work)
+{
+ struct ivtv *itv = container_of(work, struct ivtv, irq_work_queue);
+
+ DEFINE_WAIT(wait);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags))
+ vbi_work_handler(itv);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags))
+ ivtv_yuv_work_handler(itv);
+}
+
+/* Determine the required DMA size, setup enough buffers in the predma queue and
+ actually copy the data from the card to the buffers in case a PIO transfer is
+ required for this stream.
+ */
+static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_buffer *buf;
+ struct list_head *p;
+ u32 bytes_needed = 0;
+ u32 offset, size;
+ u32 UVoffset = 0, UVsize = 0;
+ int skip_bufs = s->q_predma.buffers;
+ int idx = s->SG_length;
+ int rc;
+
+ /* sanity checks */
+ if (s->v4l2dev == NULL) {
+ IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
+ return -1;
+ }
+ if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ IVTV_DEBUG_WARN("Stream %s not open\n", s->name);
+ return -1;
+ }
+
+ /* determine offset, size and PTS for the various streams */
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ offset = data[1];
+ size = data[2];
+ s->dma_pts = 0;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ offset = data[1];
+ size = data[2];
+ UVoffset = data[3];
+ UVsize = data[4];
+ s->dma_pts = ((u64) data[5] << 32) | data[6];
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ offset = data[1] + 12;
+ size = data[2] - 12;
+ s->dma_pts = read_dec(offset - 8) |
+ ((u64)(read_dec(offset - 12)) << 32);
+ if (itv->has_cx23415)
+ offset += IVTV_DECODER_OFFSET;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ size = itv->vbi.enc_size * itv->vbi.fpi;
+ offset = read_enc(itv->vbi.enc_start - 4) + 12;
+ if (offset == 12) {
+ IVTV_DEBUG_INFO("VBI offset == 0\n");
+ return -1;
+ }
+ s->dma_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
+ break;
+
+ case IVTV_DEC_STREAM_TYPE_VBI:
+ size = read_dec(itv->vbi.dec_start + 4) + 8;
+ offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
+ s->dma_pts = 0;
+ offset += IVTV_DECODER_OFFSET;
+ break;
+ default:
+ /* shouldn't happen */
+ return -1;
+ }
+
+ /* if this is the start of the DMA then fill in the magic cookie */
+ if (s->SG_length == 0) {
+ if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+ s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+ s->dma_backup = read_dec(offset - IVTV_DECODER_OFFSET);
+ write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
+ }
+ else {
+ s->dma_backup = read_enc(offset);
+ write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
+ }
+ s->dma_offset = offset;
+ }
+
+ bytes_needed = size;
+ if (s->type == IVTV_ENC_STREAM_TYPE_YUV) {
+ /* The size for the Y samples needs to be rounded upwards to a
+ multiple of the buf_size. The UV samples then start in the
+ next buffer. */
+ bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size);
+ bytes_needed += UVsize;
+ }
+
+ IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
+ ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
+
+ rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
+ if (rc < 0) { /* Insufficient buffers */
+ IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n",
+ bytes_needed, s->name);
+ return -1;
+ }
+ if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) {
+ IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
+ IVTV_WARN("Cause: the application is not reading fast enough.\n");
+ }
+ s->buffers_stolen = rc;
+
+ /* got the buffers, now fill in SGarray (DMA) or copy the data from the card
+ to the buffers (PIO). */
+ buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+ memset(buf->buf, 0, 128);
+ list_for_each(p, &s->q_predma.list) {
+ struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+
+ if (skip_bufs-- > 0)
+ continue;
+ if (!ivtv_use_pio(s)) {
+ s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle);
+ s->SGarray[idx].src = cpu_to_le32(offset);
+ s->SGarray[idx].size = cpu_to_le32(s->buf_size);
+ }
+ buf->bytesused = (size < s->buf_size) ? size : s->buf_size;
+
+ /* If PIO, then copy the data from the card to the buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused);
+ }
+ else if (ivtv_use_pio(s)) {
+ memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused);
+ }
+
+ s->q_predma.bytesused += buf->bytesused;
+ size -= buf->bytesused;
+ offset += s->buf_size;
+
+ /* Sync SG buffers */
+ ivtv_buf_sync_for_device(s, buf);
+
+ if (size == 0) { /* YUV */
+ /* process the UV section */
+ offset = UVoffset;
+ size = UVsize;
+ }
+ idx++;
+ }
+ s->SG_length = idx;
+ return 0;
+}
+
+static void dma_post(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_buffer *buf = NULL;
+ struct list_head *p;
+ u32 offset;
+ u32 *u32buf;
+ int x = 0;
+
+ if (ivtv_use_pio(s)) {
+ if (s->q_predma.bytesused)
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+ s->SG_length = 0;
+ }
+ IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
+ s->name, s->dma_offset);
+ list_for_each(p, &s->q_dma.list) {
+ buf = list_entry(p, struct ivtv_buffer, list);
+ u32buf = (u32 *)buf->buf;
+
+ /* Sync Buffer */
+ ivtv_buf_sync_for_cpu(s, buf);
+
+ if (x == 0) {
+ offset = s->dma_last_offset;
+ if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
+ {
+ for (offset = 0; offset < 64; offset++) {
+ if (u32buf[offset] == DMA_MAGIC_COOKIE) {
+ break;
+ }
+ }
+ offset *= 4;
+ if (offset == 256) {
+ IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
+ offset = s->dma_last_offset;
+ }
+ if (s->dma_last_offset != offset)
+ IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset);
+ s->dma_last_offset = offset;
+ }
+ if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+ s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+ write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET);
+ }
+ else {
+ write_enc_sync(0, s->dma_offset);
+ }
+ if (offset) {
+ buf->bytesused -= offset;
+ memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset);
+ }
+ *u32buf = cpu_to_le32(s->dma_backup);
+ }
+ x++;
+ /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
+ s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ set_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags);
+ }
+ if (buf)
+ buf->bytesused += s->dma_last_offset;
+ if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ /* Parse and Groom VBI Data */
+ s->q_dma.bytesused -= buf->bytesused;
+ ivtv_process_vbi_data(itv, buf, 0, s->type);
+ s->q_dma.bytesused += buf->bytesused;
+ if (s->id == -1) {
+ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+ return;
+ }
+ }
+ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused);
+ if (s->id != -1)
+ wake_up(&s->waitq);
+}
+
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_buffer *buf;
+ struct list_head *p;
+ u32 y_size = itv->params.height * itv->params.width;
+ u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
+ int y_done = 0;
+ int bytes_written = 0;
+ unsigned long flags = 0;
+ int idx = 0;
+
+ IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
+ buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+ list_for_each(p, &s->q_predma.list) {
+ struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+
+ /* YUV UV Offset from Y Buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) {
+ offset = uv_offset;
+ y_done = 1;
+ }
+ s->SGarray[idx].src = cpu_to_le32(buf->dma_handle);
+ s->SGarray[idx].dst = cpu_to_le32(offset);
+ s->SGarray[idx].size = cpu_to_le32(buf->bytesused);
+
+ offset += buf->bytesused;
+ bytes_written += buf->bytesused;
+
+ /* Sync SG buffers */
+ ivtv_buf_sync_for_device(s, buf);
+ idx++;
+ }
+ s->SG_length = idx;
+
+ /* Mark last buffer size for Interrupt flag */
+ s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+ /* Sync Hardware SG List of buffers */
+ ivtv_stream_sync_for_device(s);
+ if (lock)
+ spin_lock_irqsave(&itv->dma_reg_lock, flags);
+ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+ ivtv_dma_dec_start(s);
+ }
+ else {
+ set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+ }
+ if (lock)
+ spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
+
+/* start the encoder DMA */
+static void ivtv_dma_enc_start(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ int i;
+
+ if (s->q_predma.bytesused)
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+ IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
+ s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256);
+
+ /* If this is an MPEG stream, and VBI data is also pending, then append the
+ VBI DMA to the MPEG DMA and transfer both sets of data at once.
+
+ VBI DMA is a second class citizen compared to MPEG and mixing them together
+ will confuse the firmware (the end of a VBI DMA is seen as the end of a
+ MPEG DMA, thus effectively dropping an MPEG frame). So instead we make
+ sure we only use the MPEG DMA to transfer the VBI DMA if both are in
+ use. This way no conflicts occur. */
+ clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length &&
+ s->SG_length + s_vbi->SG_length <= s->buffers) {
+ ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
+ s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256);
+ for (i = 0; i < s_vbi->SG_length; i++) {
+ s->SGarray[s->SG_length++] = s_vbi->SGarray[i];
+ }
+ itv->vbi.dma_offset = s_vbi->dma_offset;
+ s_vbi->SG_length = 0;
+ set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+ IVTV_DEBUG_DMA("include DMA for %s\n", s->name);
+ }
+
+ /* Mark last buffer size for Interrupt flag */
+ s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+ /* Sync Hardware SG List of buffers */
+ ivtv_stream_sync_for_device(s);
+ write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR);
+ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
+ set_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = s->type;
+ itv->dma_timer.expires = jiffies + HZ / 10;
+ add_timer(&itv->dma_timer);
+}
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ if (s->q_predma.bytesused)
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+ IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
+ /* put SG Handle into register 0x0c */
+ write_reg(s->SG_handle, IVTV_REG_DECDMAADDR);
+ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+ set_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = s->type;
+ itv->dma_timer.expires = jiffies + HZ / 10;
+ add_timer(&itv->dma_timer);
+}
+
+static void ivtv_irq_dma_read(struct ivtv *itv)
+{
+ struct ivtv_stream *s = NULL;
+ struct ivtv_buffer *buf;
+ int hw_stream_type;
+
+ IVTV_DEBUG_IRQ("DEC DMA READ\n");
+ del_timer(&itv->dma_timer);
+ if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
+ IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS));
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ }
+ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+ hw_stream_type = 2;
+ }
+ else {
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+ hw_stream_type = 0;
+ }
+ IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
+
+ ivtv_stream_sync_for_cpu(s);
+
+ /* For some reason must kick the firmware, like PIO mode,
+ I think this tells the firmware we are done and the size
+ of the xfer so it can calculate what we need next.
+ I think we can do this part ourselves but would have to
+ fully calculate xfer info ourselves and not use interrupts
+ */
+ ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,
+ hw_stream_type);
+
+ /* Free last DMA call */
+ while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {
+ ivtv_buf_sync_for_cpu(s, buf);
+ ivtv_enqueue(s, buf, &s->q_free);
+ }
+ wake_up(&s->waitq);
+ }
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ del_timer(&itv->dma_timer);
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
+ IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]);
+ if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags))
+ data[1] = 3;
+ else if (data[1] > 2)
+ return;
+ s = &itv->streams[ivtv_stream_map[data[1]]];
+ if (data[0] & 0x18) {
+ IVTV_DEBUG_WARN("ENC DMA ERROR %x\n", data[0]);
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[1]);
+ }
+ s->SG_length = 0;
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ dma_post(s);
+ ivtv_stream_sync_for_cpu(s);
+ if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+ u32 tmp;
+
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ tmp = s->dma_offset;
+ s->dma_offset = itv->vbi.dma_offset;
+ dma_post(s);
+ s->dma_offset = tmp;
+ }
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_dma_err(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ del_timer(&itv->dma_timer);
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
+ IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
+ read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
+ itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
+ struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
+
+ /* retry */
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_dma_dec_start(s);
+ else
+ ivtv_dma_enc_start(s);
+ return;
+ }
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_start_cap(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ /* Get DMA destination and size arguments from card */
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data);
+ IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
+
+ if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
+ IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
+ data[0], data[1], data[2]);
+ return;
+ }
+ clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
+ s = &itv->streams[ivtv_stream_map[data[0]]];
+ if (!stream_enc_dma_append(s, data)) {
+ if (ivtv_use_pio(s)) {
+ dma_post(s);
+ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]);
+ }
+ else {
+ set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+ }
+ }
+}
+
+static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
+{
+ struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ IVTV_DEBUG_IRQ("ENC START VBI CAP\n");
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+ if (ivtv_use_pio(s)) {
+ if (stream_enc_dma_append(s, data))
+ return;
+ if (s->q_predma.bytesused)
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+ s->SG_length = 0;
+ dma_post(s);
+ return;
+ }
+ /* If more than two VBI buffers are pending, then
+ clear the old ones and start with this new one.
+ This can happen during transition stages when MPEG capturing is
+ started, but the first interrupts haven't arrived yet. During
+ that period VBI requests can accumulate without being able to
+ DMA the data. Since at most four VBI DMA buffers are available,
+ we just drop the old requests when there are already three
+ requests queued. */
+ if (s->SG_length > 2) {
+ struct list_head *p;
+ list_for_each(p, &s->q_predma.list) {
+ struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+ ivtv_buf_sync_for_cpu(s, buf);
+ }
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
+ s->SG_length = 0;
+ }
+ /* if we can append the data, and the MPEG stream isn't capturing,
+ then start a DMA request for just the VBI data. */
+ if (!stream_enc_dma_append(s, data) &&
+ !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) {
+ set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
+ set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+ }
+}
+
+static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+
+ IVTV_DEBUG_IRQ("DEC VBI REINSERT\n");
+ if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
+ !stream_enc_dma_append(s, data)) {
+ dma_post(s);
+ }
+}
+
+static void ivtv_irq_dec_data_req(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ /* YUV or MPG */
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+ itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2;
+ itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0];
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+ }
+ else {
+ itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2];
+ itv->dma_data_req_offset = data[1];
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+ }
+ IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
+ itv->dma_data_req_offset, itv->dma_data_req_size);
+ if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
+ set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ }
+ else {
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+ ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
+ }
+}
+
+static void ivtv_irq_vsync(struct ivtv *itv)
+{
+ /* The vsync interrupt is unusual in that it won't clear until
+ * the end of the first line for the current field, at which
+ * point it clears itself. This can result in repeated vsync
+ * interrupts, or a missed vsync. Read some of the registers
+ * to determine the line being displayed and ensure we handle
+ * one vsync per frame.
+ */
+ unsigned int frame = read_reg(0x28c0) & 1;
+ int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
+
+ if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
+
+ if (((frame ^ itv->yuv_info.lace_sync_field) == 0 && ((itv->lastVsyncFrame & 1) ^ itv->yuv_info.lace_sync_field)) ||
+ (frame != (itv->lastVsyncFrame & 1) && !itv->yuv_info.frame_interlaced)) {
+ int next_dma_frame = last_dma_frame;
+
+ if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) {
+ write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
+ write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+ write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
+ write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+ next_dma_frame = (next_dma_frame + 1) & 0x3;
+ atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame);
+ }
+ }
+ if (frame != (itv->lastVsyncFrame & 1)) {
+ struct ivtv_stream *s = ivtv_get_output_stream(itv);
+ int work = 0;
+
+ itv->lastVsyncFrame += 1;
+ if (frame == 0) {
+ clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+ clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+ }
+ else {
+ set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+ }
+ if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) {
+ set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags);
+ wake_up(&itv->event_waitq);
+ }
+ wake_up(&itv->vsync_waitq);
+ if (s)
+ wake_up(&s->waitq);
+
+ /* Send VBI to saa7127 */
+ if (frame) {
+ set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags);
+ work = 1;
+ }
+
+ /* Check if we need to update the yuv registers */
+ if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
+ if (!itv->yuv_info.new_frame_info[last_dma_frame].update)
+ last_dma_frame = (last_dma_frame - 1) & 3;
+
+ if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) {
+ itv->yuv_info.update_frame = last_dma_frame;
+ itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
+ itv->yuv_info.yuv_forced_update = 0;
+ set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
+ work = 1;
+ }
+ }
+ if (work)
+ queue_work(itv->irq_work_queues, &itv->irq_work_queue);
+ }
+}
+
+#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ)
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
+{
+ struct ivtv *itv = (struct ivtv *)dev_id;
+ u32 combo;
+ u32 stat;
+ int i;
+ u8 vsync_force = 0;
+
+ spin_lock(&itv->dma_reg_lock);
+ /* get contents of irq status register */
+ stat = read_reg(IVTV_REG_IRQSTATUS);
+
+ combo = ~itv->irqmask & stat;
+
+ /* Clear out IRQ */
+ if (combo) write_reg(combo, IVTV_REG_IRQSTATUS);
+
+ if (0 == combo) {
+ /* The vsync interrupt is unusual and clears itself. If we
+ * took too long, we may have missed it. Do some checks
+ */
+ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+ /* vsync is enabled, see if we're in a new field */
+ if ((itv->lastVsyncFrame & 1) != (read_reg(0x28c0) & 1)) {
+ /* New field, looks like we missed it */
+ IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16);
+ vsync_force = 1;
+ }
+ }
+
+ if (!vsync_force) {
+ /* No Vsync expected, wasn't for us */
+ spin_unlock(&itv->dma_reg_lock);
+ return IRQ_NONE;
+ }
+ }
+
+ /* Exclude interrupts noted below from the output, otherwise the log is flooded with
+ these messages */
+ if (combo & ~0xff6d0400)
+ IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
+
+ if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
+ IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n");
+ }
+
+ if (combo & IVTV_IRQ_DMA_READ) {
+ ivtv_irq_dma_read(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
+ ivtv_irq_enc_dma_complete(itv);
+ }
+
+ if (combo & IVTV_IRQ_DMA_ERR) {
+ ivtv_irq_dma_err(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_START_CAP) {
+ ivtv_irq_enc_start_cap(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_VBI_CAP) {
+ ivtv_irq_enc_vbi_cap(itv);
+ }
+
+ if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
+ ivtv_irq_dev_vbi_reinsert(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_EOS) {
+ IVTV_DEBUG_IRQ("ENC EOS\n");
+ set_bit(IVTV_F_I_EOS, &itv->i_flags);
+ wake_up(&itv->cap_w);
+ }
+
+ if (combo & IVTV_IRQ_DEC_DATA_REQ) {
+ ivtv_irq_dec_data_req(itv);
+ }
+
+ /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */
+ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+ ivtv_irq_vsync(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_VIM_RST) {
+ IVTV_DEBUG_IRQ("VIM RST\n");
+ /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */
+ }
+
+ if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
+ IVTV_DEBUG_INFO("Stereo mode changed\n");
+ }
+
+ if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS;
+ struct ivtv_stream *s = &itv->streams[idx];
+
+ if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
+ continue;
+ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_dma_dec_start(s);
+ else
+ ivtv_dma_enc_start(s);
+ break;
+ }
+ if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) {
+ ivtv_udma_start(itv);
+ }
+ }
+
+ spin_unlock(&itv->dma_reg_lock);
+
+ /* If we've just handled a 'forced' vsync, it's safest to say it
+ * wasn't ours. Another device may have triggered it at just
+ * the right time.
+ */
+ return vsync_force ? IRQ_NONE : IRQ_HANDLED;
+}
+
+void ivtv_unfinished_dma(unsigned long arg)
+{
+ struct ivtv *itv = (struct ivtv *)arg;
+
+ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+ return;
+ IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/video/ivtv/ivtv-irq.h
new file mode 100644
index 000000000000..a43348a30309
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-irq.h
@@ -0,0 +1,26 @@
+/*
+ interrupt handling
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id);
+
+void ivtv_irq_work_handler(struct work_struct *work);
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock);
+void ivtv_unfinished_dma(unsigned long arg);
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c
new file mode 100644
index 000000000000..6ae42a3b03cc
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-mailbox.c
@@ -0,0 +1,360 @@
+/*
+ mailbox functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 <stdarg.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+
+/* Firmware mailbox flags*/
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE 0x00000002
+#define IVTV_MBOX_DRIVER_BUSY 0x00000001
+#define IVTV_MBOX_FREE 0x00000000
+
+/* Firmware mailbox standard timeout */
+#define IVTV_API_STD_TIMEOUT 0x02000000
+
+#define API_CACHE (1 << 0) /* Allow the command to be stored in the cache */
+#define API_RESULT (1 << 1) /* Allow 1 second for this cmd to end */
+#define API_FAST_RESULT (3 << 1) /* Allow 0.1 second for this cmd to end */
+#define API_DMA (1 << 3) /* DMA mailbox, has special handling */
+#define API_NO_WAIT_MB (1 << 4) /* Command may not wait for a free mailbox */
+#define API_NO_WAIT_RES (1 << 5) /* Command may not wait for the result */
+
+struct ivtv_api_info {
+ int flags; /* Flags, see above */
+ const char *name; /* The name of the command */
+};
+
+#define API_ENTRY(x, f) [x] = { (f), #x }
+
+static const struct ivtv_api_info api_info[256] = {
+ /* MPEG encoder API */
+ API_ENTRY(CX2341X_ENC_PING_FW, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_START_CAPTURE, API_RESULT),
+ API_ENTRY(CX2341X_ENC_STOP_CAPTURE, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_PCR_ID, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_BIT_RATE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_VBI_LINE, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, API_CACHE),
+ API_ENTRY(CX2341X_ENC_HALT_FW, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_GET_VERSION, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_GET_SEQ_END, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, API_DMA),
+ API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, API_RESULT),
+ API_ENTRY(CX2341X_ENC_REFRESH_INPUT, API_NO_WAIT_MB),
+ API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, API_CACHE),
+ API_ENTRY(CX2341X_ENC_MUTE_VIDEO, API_RESULT),
+ API_ENTRY(CX2341X_ENC_MUTE_AUDIO, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_MISC, API_FAST_RESULT),
+ /* Obsolete PULLDOWN API command */
+ API_ENTRY(0xb1, API_CACHE),
+
+ /* MPEG decoder API */
+ API_ENTRY(CX2341X_DEC_PING_FW, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_START_PLAYBACK, API_RESULT),
+ API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, API_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, API_RESULT),
+ API_ENTRY(CX2341X_DEC_STEP_VIDEO, API_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, API_CACHE),
+ API_ENTRY(CX2341X_DEC_GET_XFER_INFO, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, API_DMA),
+ API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, API_RESULT),
+ API_ENTRY(CX2341X_DEC_HALT_FW, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_STANDARD, API_CACHE),
+ API_ENTRY(CX2341X_DEC_GET_VERSION, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, API_CACHE),
+ API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, API_RESULT /*| API_NO_WAIT_RES*/),
+ API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, API_CACHE),
+ API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, API_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, API_CACHE),
+ API_ENTRY(CX2341X_DEC_EXTRACT_VBI, API_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, API_CACHE),
+
+ /* OSD API */
+ API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_STATE, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_STATE, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, API_CACHE),
+ API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, API_CACHE),
+ API_ENTRY(CX2341X_OSD_BLT_COPY, API_RESULT),
+ API_ENTRY(CX2341X_OSD_BLT_FILL, API_RESULT),
+ API_ENTRY(CX2341X_OSD_BLT_TEXT, API_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, API_CACHE),
+ API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, API_CACHE)
+};
+
+static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb)
+{
+ u32 flags = readl(&mbdata->mbox[mb].flags);
+ int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE);
+
+ /* if the mailbox is free, then try to claim it */
+ if (is_free && !test_and_set_bit(mb, &mbdata->busy)) {
+ write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags);
+ return 1;
+ }
+ return 0;
+}
+
+/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not
+ attempted here. */
+static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags)
+{
+ unsigned long then = jiffies;
+ int i, mb;
+ int max_mbox = mbdata->max_mbox;
+ int retries = 100;
+
+ /* All slow commands use the same mailbox, serializing them and also
+ leaving the other mailbox free for simple fast commands. */
+ if ((flags & API_FAST_RESULT) == API_RESULT)
+ max_mbox = 1;
+
+ /* find free non-DMA mailbox */
+ for (i = 0; i < retries; i++) {
+ for (mb = 1; mb <= max_mbox; mb++)
+ if (try_mailbox(itv, mbdata, mb))
+ return mb;
+
+ /* Sleep before a retry, if not atomic */
+ if (!(flags & API_NO_WAIT_MB)) {
+ if (jiffies - then > retries * HZ / 100)
+ break;
+ ivtv_sleep_timeout(HZ / 100, 0);
+ }
+ }
+ return -ENODEV;
+}
+
+static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[])
+{
+ int i;
+
+ write_sync(cmd, &mbox->cmd);
+ write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout);
+
+ for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+ write_sync(data[i], &mbox->data[i]);
+
+ write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags);
+}
+
+static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata)
+{
+ int i;
+
+ for (i = 0; i <= mbdata->max_mbox; i++) {
+ IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n",
+ i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags));
+ write_sync(0, &mbdata->mbox[i].flags);
+ clear_bit(i, &mbdata->busy);
+ }
+}
+
+static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+ struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox;
+ volatile struct ivtv_mailbox __iomem *mbox;
+ int api_timeout = HZ;
+ int flags, mb, i;
+ unsigned long then;
+
+ /* sanity checks */
+ if (NULL == mbdata) {
+ IVTV_ERR("No mailbox allocated\n");
+ return -ENODEV;
+ }
+ if (args < 0 || args > CX2341X_MBOX_MAX_DATA ||
+ cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) {
+ IVTV_ERR("Invalid API call: cmd = 0x%02x, args = %d\n", cmd, args);
+ return -EINVAL;
+ }
+
+ IVTV_DEBUG_API("API Call: %s\n", api_info[cmd].name);
+
+ /* clear possibly uninitialized part of data array */
+ for (i = args; i < CX2341X_MBOX_MAX_DATA; i++)
+ data[i] = 0;
+
+ /* If this command was issued within the last 30 minutes and with identical
+ data, then just return 0 as there is no need to issue this command again.
+ Just an optimization to prevent unnecessary use of mailboxes. */
+ if (itv->api_cache[cmd].last_jiffies &&
+ jiffies - itv->api_cache[cmd].last_jiffies < HZ * 1800 &&
+ !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) {
+ itv->api_cache[cmd].last_jiffies = jiffies;
+ return 0;
+ }
+
+ flags = api_info[cmd].flags;
+
+ if (flags & API_DMA) {
+ for (i = 0; i < 100; i++) {
+ mb = i % (mbdata->max_mbox + 1);
+ if (try_mailbox(itv, mbdata, mb)) {
+ write_mailbox(&mbdata->mbox[mb], cmd, args, data);
+ clear_bit(mb, &mbdata->busy);
+ return 0;
+ }
+ IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n",
+ api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags));
+ }
+ IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name);
+ clear_all_mailboxes(itv, mbdata);
+ return -EBUSY;
+ }
+
+ if ((flags & API_FAST_RESULT) == API_FAST_RESULT)
+ api_timeout = HZ / 10;
+
+ mb = get_mailbox(itv, mbdata, flags);
+ if (mb < 0) {
+ IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name);
+ clear_all_mailboxes(itv, mbdata);
+ return -EBUSY;
+ }
+ mbox = &mbdata->mbox[mb];
+ write_mailbox(mbox, cmd, args, data);
+ if (flags & API_CACHE) {
+ memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data));
+ itv->api_cache[cmd].last_jiffies = jiffies;
+ }
+ if ((flags & API_RESULT) == 0) {
+ clear_bit(mb, &mbdata->busy);
+ return 0;
+ }
+
+ /* Get results */
+ then = jiffies;
+
+ while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) {
+ if (jiffies - then > api_timeout) {
+ IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name);
+ /* reset the mailbox, but it is likely too late already */
+ write_sync(0, &mbox->flags);
+ clear_bit(mb, &mbdata->busy);
+ return -EIO;
+ }
+ if (flags & API_NO_WAIT_RES)
+ mdelay(1);
+ else
+ ivtv_sleep_timeout(HZ / 100, 0);
+ }
+ if (jiffies - then > HZ / 10)
+ IVTV_DEBUG_WARN("%s took %lu jiffies (%d per HZ)\n",
+ api_info[cmd].name, jiffies - then, HZ);
+
+ for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+ data[i] = readl(&mbox->data[i]);
+ write_sync(0, &mbox->flags);
+ clear_bit(mb, &mbdata->busy);
+ return 0;
+}
+
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+ int res = ivtv_api_call(itv, cmd, args, data);
+
+ /* Allow a single retry, probably already too late though.
+ If there is no free mailbox then that is usually an indication
+ of a more serious problem. */
+ return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res;
+}
+
+int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ return ivtv_api(priv, cmd, in, data);
+}
+
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...)
+{
+ va_list ap;
+ int i;
+
+ va_start(ap, args);
+ for (i = 0; i < args; i++) {
+ data[i] = va_arg(ap, u32);
+ }
+ va_end(ap);
+ return ivtv_api(itv, cmd, args, data);
+}
+
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list ap;
+ int i;
+
+ va_start(ap, args);
+ for (i = 0; i < args; i++) {
+ data[i] = va_arg(ap, u32);
+ }
+ va_end(ap);
+ return ivtv_api(itv, cmd, args, data);
+}
+
+/* This one is for stuff that can't sleep.. irq handlers, etc.. */
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[])
+{
+ int i;
+
+ for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+ data[i] = readl(&mbdata->mbox[mb].data[i]);
+}
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h
new file mode 100644
index 000000000000..79b8aec14370
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-mailbox.h
@@ -0,0 +1,25 @@
+/*
+ mailbox functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]);
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]);
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...);
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...);
+int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);
diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c
new file mode 100644
index 000000000000..ccfcef1ad91a
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-queue.c
@@ -0,0 +1,262 @@
+/*
+ buffer queues.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-streams.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
+{
+ if (s->buf_size - buf->bytesused < copybytes)
+ copybytes = s->buf_size - buf->bytesused;
+ if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
+ return -EFAULT;
+ }
+ buf->bytesused += copybytes;
+ return copybytes;
+}
+
+void ivtv_buf_swap(struct ivtv_buffer *buf)
+{
+ int i;
+
+ for (i = 0; i < buf->bytesused; i += 4)
+ swab32s((u32 *)(buf->buf + i));
+}
+
+void ivtv_queue_init(struct ivtv_queue *q)
+{
+ INIT_LIST_HEAD(&q->list);
+ q->buffers = 0;
+ q->length = 0;
+ q->bytesused = 0;
+}
+
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
+{
+ unsigned long flags = 0;
+
+ /* clear the buffer if it is going to be enqueued to the free queue */
+ if (q == &s->q_free) {
+ buf->bytesused = 0;
+ buf->readpos = 0;
+ buf->b_flags = 0;
+ }
+ spin_lock_irqsave(&s->qlock, flags);
+ list_add_tail(&buf->list, &q->list);
+ q->buffers++;
+ q->length += s->buf_size;
+ q->bytesused += buf->bytesused - buf->readpos;
+ spin_unlock_irqrestore(&s->qlock, flags);
+}
+
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
+{
+ struct ivtv_buffer *buf = NULL;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&s->qlock, flags);
+ if (!list_empty(&q->list)) {
+ buf = list_entry(q->list.next, struct ivtv_buffer, list);
+ list_del_init(q->list.next);
+ q->buffers--;
+ q->length -= s->buf_size;
+ q->bytesused -= buf->bytesused - buf->readpos;
+ }
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return buf;
+}
+
+static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
+ struct ivtv_queue *to, int clear, int full)
+{
+ struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
+
+ list_move_tail(from->list.next, &to->list);
+ from->buffers--;
+ from->length -= s->buf_size;
+ from->bytesused -= buf->bytesused - buf->readpos;
+ /* special handling for q_free */
+ if (clear)
+ buf->bytesused = buf->readpos = buf->b_flags = 0;
+ else if (full) {
+ /* special handling for stolen buffers, assume
+ all bytes are used. */
+ buf->bytesused = s->buf_size;
+ buf->readpos = buf->b_flags = 0;
+ }
+ to->buffers++;
+ to->length += s->buf_size;
+ to->bytesused += buf->bytesused - buf->readpos;
+}
+
+/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
+ If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
+ If 'steal' != NULL, then buffers may also taken from that queue if
+ needed.
+
+ The buffer is automatically cleared if it goes to the free queue. It is
+ also cleared if buffers need to be taken from the 'steal' queue and
+ the 'from' queue is the free queue.
+
+ When 'from' is q_free, then needed_bytes is compared to the total
+ available buffer length, otherwise needed_bytes is compared to the
+ bytesused value. For the 'steal' queue the total available buffer
+ length is always used.
+
+ -ENOMEM is returned if the buffers could not be obtained, 0 if all
+ buffers where obtained from the 'from' list and if non-zero then
+ the number of stolen buffers is returned. */
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+ struct ivtv_queue *to, int needed_bytes)
+{
+ unsigned long flags;
+ int rc = 0;
+ int from_free = from == &s->q_free;
+ int to_free = to == &s->q_free;
+ int bytes_available;
+
+ spin_lock_irqsave(&s->qlock, flags);
+ if (needed_bytes == 0) {
+ from_free = 1;
+ needed_bytes = from->length;
+ }
+
+ bytes_available = from_free ? from->length : from->bytesused;
+ bytes_available += steal ? steal->length : 0;
+
+ if (bytes_available < needed_bytes) {
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return -ENOMEM;
+ }
+ if (from_free) {
+ u32 old_length = to->length;
+
+ while (to->length - old_length < needed_bytes) {
+ if (list_empty(&from->list))
+ from = steal;
+ if (from == steal)
+ rc++; /* keep track of 'stolen' buffers */
+ ivtv_queue_move_buf(s, from, to, 1, 0);
+ }
+ }
+ else {
+ u32 old_bytesused = to->bytesused;
+
+ while (to->bytesused - old_bytesused < needed_bytes) {
+ if (list_empty(&from->list))
+ from = steal;
+ if (from == steal)
+ rc++; /* keep track of 'stolen' buffers */
+ ivtv_queue_move_buf(s, from, to, to_free, rc);
+ }
+ }
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return rc;
+}
+
+void ivtv_flush_queues(struct ivtv_stream *s)
+{
+ ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
+ ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
+ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
+}
+
+int ivtv_stream_alloc(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ int SGsize = sizeof(struct ivtv_SG_element) * s->buffers;
+ int i;
+
+ if (s->buffers == 0)
+ return 0;
+
+ IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
+ s->dma != PCI_DMA_NONE ? "DMA " : "",
+ s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
+
+ /* Allocate DMA SG Arrays */
+ if (s->dma != PCI_DMA_NONE) {
+ s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL);
+ if (s->SGarray == NULL) {
+ IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name);
+ return -ENOMEM;
+ }
+ s->SG_length = 0;
+ s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma);
+ ivtv_stream_sync_for_cpu(s);
+ }
+
+ /* allocate stream buffers. Initially all buffers are in q_free. */
+ for (i = 0; i < s->buffers; i++) {
+ struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL);
+
+ if (buf == NULL)
+ break;
+ buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL);
+ if (buf->buf == NULL) {
+ kfree(buf);
+ break;
+ }
+ INIT_LIST_HEAD(&buf->list);
+ if (s->dma != PCI_DMA_NONE) {
+ buf->dma_handle = pci_map_single(s->itv->dev,
+ buf->buf, s->buf_size + 256, s->dma);
+ ivtv_buf_sync_for_cpu(s, buf);
+ }
+ ivtv_enqueue(s, buf, &s->q_free);
+ }
+ if (i == s->buffers)
+ return 0;
+ IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+ ivtv_stream_free(s);
+ return -ENOMEM;
+}
+
+void ivtv_stream_free(struct ivtv_stream *s)
+{
+ struct ivtv_buffer *buf;
+
+ /* move all buffers to q_free */
+ ivtv_flush_queues(s);
+
+ /* empty q_free */
+ while ((buf = ivtv_dequeue(s, &s->q_free))) {
+ if (s->dma != PCI_DMA_NONE)
+ pci_unmap_single(s->itv->dev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+ kfree(buf->buf);
+ kfree(buf);
+ }
+
+ /* Free SG Array/Lists */
+ if (s->SGarray != NULL) {
+ if (s->SG_handle != IVTV_DMA_UNMAPPED) {
+ pci_unmap_single(s->itv->dev, s->SG_handle,
+ sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+ s->SG_handle = IVTV_DMA_UNMAPPED;
+ }
+ s->SGarray = NULL;
+ s->SG_length = 0;
+ }
+}
diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h
new file mode 100644
index 000000000000..903edd4b4381
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-queue.h
@@ -0,0 +1,64 @@
+/*
+ buffer queues.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 IVTV_DMA_UNMAPPED ((u32) -1)
+
+/* ivtv_buffer utility functions */
+static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+ if (s->dma != PCI_DMA_NONE)
+ pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+}
+
+static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+ if (s->dma != PCI_DMA_NONE)
+ pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+}
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes);
+void ivtv_buf_swap(struct ivtv_buffer *buf);
+
+/* ivtv_queue utility functions */
+void ivtv_queue_init(struct ivtv_queue *q);
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q);
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q);
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+ struct ivtv_queue *to, int needed_bytes);
+void ivtv_flush_queues(struct ivtv_stream *s);
+
+/* ivtv_stream utility functions */
+int ivtv_stream_alloc(struct ivtv_stream *s);
+void ivtv_stream_free(struct ivtv_stream *s);
+
+static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
+{
+ pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle,
+ sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
+{
+ pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle,
+ sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
new file mode 100644
index 000000000000..01a41a844a30
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -0,0 +1,977 @@
+/*
+ init/start/stop/exit stream functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+/* License: GPL
+ * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
+ *
+ * This file will hold API related functions, both internal (firmware api)
+ * and external (v4l2, etc)
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-audio.h"
+#include "ivtv-video.h"
+#include "ivtv-vbi.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-irq.h"
+#include "ivtv-streams.h"
+#include "ivtv-cards.h"
+
+static struct file_operations ivtv_v4l2_enc_fops = {
+ .owner = THIS_MODULE,
+ .read = ivtv_v4l2_read,
+ .write = ivtv_v4l2_write,
+ .open = ivtv_v4l2_open,
+ .ioctl = ivtv_v4l2_ioctl,
+ .release = ivtv_v4l2_close,
+ .poll = ivtv_v4l2_enc_poll,
+};
+
+static struct file_operations ivtv_v4l2_dec_fops = {
+ .owner = THIS_MODULE,
+ .read = ivtv_v4l2_read,
+ .write = ivtv_v4l2_write,
+ .open = ivtv_v4l2_open,
+ .ioctl = ivtv_v4l2_ioctl,
+ .release = ivtv_v4l2_close,
+ .poll = ivtv_v4l2_dec_poll,
+};
+
+static struct {
+ const char *name;
+ int vfl_type;
+ int minor_offset;
+ int dma, pio;
+ enum v4l2_buf_type buf_type;
+ struct file_operations *fops;
+} ivtv_stream_info[] = {
+ { /* IVTV_ENC_STREAM_TYPE_MPG */
+ "encoder MPEG",
+ VFL_TYPE_GRABBER, 0,
+ PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_YUV */
+ "encoder YUV",
+ VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
+ PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_VBI */
+ "encoder VBI",
+ VFL_TYPE_VBI, 0,
+ PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_PCM */
+ "encoder PCM audio",
+ VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
+ PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_RAD */
+ "encoder radio",
+ VFL_TYPE_RADIO, 0,
+ PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_MPG */
+ "decoder MPEG",
+ VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
+ PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ &ivtv_v4l2_dec_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_VBI */
+ "decoder VBI",
+ VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
+ PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_VOUT */
+ "decoder VOUT",
+ VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
+ PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT,
+ &ivtv_v4l2_dec_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_YUV */
+ "decoder YUV",
+ VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
+ PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ &ivtv_v4l2_dec_fops
+ }
+};
+
+static void ivtv_stream_init(struct ivtv *itv, int type)
+{
+ struct ivtv_stream *s = &itv->streams[type];
+ struct video_device *dev = s->v4l2dev;
+
+ /* we need to keep v4l2dev, so restore it afterwards */
+ memset(s, 0, sizeof(*s));
+ s->v4l2dev = dev;
+
+ /* initialize ivtv_stream fields */
+ s->itv = itv;
+ s->type = type;
+ s->name = ivtv_stream_info[type].name;
+
+ if (ivtv_stream_info[type].pio)
+ s->dma = PCI_DMA_NONE;
+ else
+ s->dma = ivtv_stream_info[type].dma;
+ s->buf_size = itv->stream_buf_size[type];
+ if (s->buf_size)
+ s->buffers = itv->options.megabytes[type] * 1024 * 1024 / s->buf_size;
+ spin_lock_init(&s->qlock);
+ init_waitqueue_head(&s->waitq);
+ s->id = -1;
+ s->SG_handle = IVTV_DMA_UNMAPPED;
+ ivtv_queue_init(&s->q_free);
+ ivtv_queue_init(&s->q_full);
+ ivtv_queue_init(&s->q_dma);
+ ivtv_queue_init(&s->q_predma);
+ ivtv_queue_init(&s->q_io);
+}
+
+static int ivtv_reg_dev(struct ivtv *itv, int type)
+{
+ struct ivtv_stream *s = &itv->streams[type];
+ int vfl_type = ivtv_stream_info[type].vfl_type;
+ int minor_offset = ivtv_stream_info[type].minor_offset;
+ int minor;
+
+ /* These four fields are always initialized. If v4l2dev == NULL, then
+ this stream is not in use. In that case no other fields but these
+ four can be used. */
+ s->v4l2dev = NULL;
+ s->itv = itv;
+ s->type = type;
+ s->name = ivtv_stream_info[type].name;
+
+ /* Check whether the radio is supported */
+ if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO))
+ return 0;
+ if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return 0;
+
+ if (minor_offset >= 0)
+ /* card number + user defined offset + device offset */
+ minor = itv->num + ivtv_first_minor + minor_offset;
+ else
+ minor = -1;
+
+ /* User explicitly selected 0 buffers for these streams, so don't
+ create them. */
+ if (minor >= 0 && ivtv_stream_info[type].dma != PCI_DMA_NONE &&
+ itv->options.megabytes[type] == 0) {
+ IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
+ return 0;
+ }
+
+ ivtv_stream_init(itv, type);
+
+ /* allocate and initialize the v4l2 video device structure */
+ s->v4l2dev = video_device_alloc();
+ if (s->v4l2dev == NULL) {
+ IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
+ return -ENOMEM;
+ }
+
+ s->v4l2dev->type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT |
+ VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER;
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ s->v4l2dev->type |= VID_TYPE_MPEG_DECODER;
+ }
+ snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
+ itv->num, s->name);
+
+ s->v4l2dev->minor = minor;
+ s->v4l2dev->dev = &itv->dev->dev;
+ s->v4l2dev->fops = ivtv_stream_info[type].fops;
+ s->v4l2dev->release = video_device_release;
+
+ if (minor >= 0) {
+ /* Register device. First try the desired minor, then any free one. */
+ if (video_register_device(s->v4l2dev, vfl_type, minor) &&
+ video_register_device(s->v4l2dev, vfl_type, -1)) {
+ IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n",
+ s->name, minor);
+ video_device_release(s->v4l2dev);
+ s->v4l2dev = NULL;
+ return -ENOMEM;
+ }
+ }
+ else {
+ /* Don't register a 'hidden' stream (OSD) */
+ IVTV_INFO("Created framebuffer stream for %s\n", s->name);
+ return 0;
+ }
+
+ switch (vfl_type) {
+ case VFL_TYPE_GRABBER:
+ IVTV_INFO("Registered device video%d for %s (%d MB)\n",
+ s->v4l2dev->minor, s->name, itv->options.megabytes[type]);
+ break;
+ case VFL_TYPE_RADIO:
+ IVTV_INFO("Registered device radio%d for %s\n",
+ s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
+ break;
+ case VFL_TYPE_VBI:
+ if (itv->options.megabytes[type])
+ IVTV_INFO("Registered device vbi%d for %s (%d MB)\n",
+ s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN,
+ s->name, itv->options.megabytes[type]);
+ else
+ IVTV_INFO("Registered device vbi%d for %s\n",
+ s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
+ break;
+ }
+ return 0;
+}
+
+/* Initialize v4l2 variables and register v4l2 devices */
+int ivtv_streams_setup(struct ivtv *itv)
+{
+ int type;
+
+ /* Setup V4L2 Devices */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+ /* Register Device */
+ if (ivtv_reg_dev(itv, type))
+ break;
+
+ if (itv->streams[type].v4l2dev == NULL)
+ continue;
+
+ /* Allocate Stream */
+ if (ivtv_stream_alloc(&itv->streams[type]))
+ break;
+ }
+ if (type == IVTV_MAX_STREAMS) {
+ return 0;
+ }
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ ivtv_streams_cleanup(itv);
+ return -ENOMEM;
+}
+
+/* Unregister v4l2 devices */
+void ivtv_streams_cleanup(struct ivtv *itv)
+{
+ int type;
+
+ /* Teardown all streams */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+ struct video_device *vdev = itv->streams[type].v4l2dev;
+
+ itv->streams[type].v4l2dev = NULL;
+ if (vdev == NULL)
+ continue;
+
+ ivtv_stream_free(&itv->streams[type]);
+ /* Free Device */
+ if (vdev->minor == -1) /* 'Hidden' never registered stream (OSD) */
+ video_device_release(vdev);
+ else /* All others, just unregister. */
+ video_unregister_device(vdev);
+ }
+}
+
+static void ivtv_vbi_setup(struct ivtv *itv)
+{
+ int raw = itv->vbi.sliced_in->service_set == 0;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ int lines;
+ int i;
+
+ /* If Embed then streamtype must be Program */
+ /* TODO: should we really do this? */
+ if (0 && !raw && itv->vbi.insert_mpeg) {
+ itv->params.stream_type = 0;
+
+ /* assign stream type */
+ ivtv_vapi(itv, CX2341X_ENC_SET_STREAM_TYPE, 1, itv->params.stream_type);
+ }
+
+ /* Reset VBI */
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
+
+ if (itv->is_60hz) {
+ itv->vbi.count = 12;
+ itv->vbi.start[0] = 10;
+ itv->vbi.start[1] = 273;
+ } else { /* PAL/SECAM */
+ itv->vbi.count = 18;
+ itv->vbi.start[0] = 6;
+ itv->vbi.start[1] = 318;
+ }
+
+ /* setup VBI registers */
+ itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+
+ /* determine number of lines and total number of VBI bytes.
+ A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
+ The '- 1' byte is probably an unused U or V byte. Or something...
+ A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
+ header, 42 data bytes + checksum (to be confirmed) */
+ if (raw) {
+ lines = itv->vbi.count * 2;
+ } else {
+ lines = itv->is_60hz ? 24 : 38;
+ if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840))
+ lines += 2;
+ }
+
+ itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+
+ /* Note: sliced vs raw flag doesn't seem to have any effect
+ TODO: check mode (0x02) value with older ivtv versions. */
+ data[0] = raw | 0x02 | (0xbd << 8);
+
+ /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */
+ data[1] = 1;
+ /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */
+ data[2] = raw ? 4 : 8;
+ /* The start/stop codes determine which VBI lines end up in the raw VBI data area.
+ The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line
+ is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video)
+ code. These values for raw VBI are obtained from a driver disassembly. The sliced
+ start/stop codes was deduced from this, but they do not appear in the driver.
+ Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54.
+ However, I have no idea what these values are for. */
+ if (itv->hw_flags & IVTV_HW_CX25840) {
+ /* Setup VBI for the cx25840 digitizer */
+ if (raw) {
+ data[3] = 0x20602060;
+ data[4] = 0x30703070;
+ } else {
+ data[3] = 0xB0F0B0F0;
+ data[4] = 0xA0E0A0E0;
+ }
+ /* Lines per frame */
+ data[5] = lines;
+ /* bytes per line */
+ data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+ } else {
+ /* Setup VBI for the saa7115 digitizer */
+ if (raw) {
+ data[3] = 0x25256262;
+ data[4] = 0x387F7F7F;
+ } else {
+ data[3] = 0xABABECEC;
+ data[4] = 0xB6F1F1F1;
+ }
+ /* Lines per frame */
+ data[5] = lines;
+ /* bytes per line */
+ data[6] = itv->vbi.enc_size / lines;
+ }
+
+ IVTV_DEBUG_INFO(
+ "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n",
+ data[0], data[1], data[2], data[5], data[6]);
+
+ ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data);
+
+ /* returns the VBI encoder memory area. */
+ itv->vbi.enc_start = data[2];
+ itv->vbi.fpi = data[0];
+ if (!itv->vbi.fpi)
+ itv->vbi.fpi = 1;
+
+ IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d lines 0x%08x\n",
+ itv->vbi.enc_start, data[1], itv->vbi.fpi, itv->digitizer);
+
+ /* select VBI lines.
+ Note that the sliced argument seems to have no effect. */
+ for (i = 2; i <= 24; i++) {
+ int valid;
+
+ if (itv->is_60hz) {
+ valid = i >= 10 && i < 22;
+ } else {
+ valid = i >= 6 && i < 24;
+ }
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1,
+ valid, 0 , 0, 0);
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000,
+ valid, 0, 0, 0);
+ }
+
+ /* Remaining VBI questions:
+ - Is it possible to select particular VBI lines only for inclusion in the MPEG
+ stream? Currently you can only get the first X lines.
+ - Is mixed raw and sliced VBI possible?
+ - What's the meaning of the raw/sliced flag?
+ - What's the meaning of params 2, 3 & 4 of the Select VBI command? */
+}
+
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv *itv = s->itv;
+ int captype = 0, subtype = 0;
+ int enable_passthrough = 0;
+
+ if (s->v4l2dev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ captype = 0;
+ subtype = 3;
+
+ /* Stop Passthrough */
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ ivtv_passthrough_mode(itv, 0);
+ enable_passthrough = 1;
+ }
+ itv->mpg_data_received = itv->vbi_data_inserted = 0;
+ itv->dualwatch_jiffies = jiffies;
+ itv->dualwatch_stereo_mode = itv->params.audio_properties & 0x0300;
+ itv->search_pack_header = 0;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ captype = 2;
+ subtype = 11; /* video+audio+decoder */
+ break;
+ }
+ captype = 1;
+ subtype = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ captype = 1;
+ subtype = 2;
+ break;
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ captype = 1;
+ subtype = 4;
+
+ itv->vbi.frame = 0;
+ itv->vbi.inserted_frame = 0;
+ memset(itv->vbi.sliced_mpeg_size,
+ 0, sizeof(itv->vbi.sliced_mpeg_size));
+ break;
+ default:
+ return -EINVAL;
+ }
+ s->subtype = subtype;
+ s->buffers_stolen = 0;
+
+ /* mute/unmute video */
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 1 : 0);
+
+ /* Clear Streamoff flags in case left from last capture */
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ if (atomic_read(&itv->capturing) == 0) {
+ /* Always use frame based mode. Experiments have demonstrated that byte
+ stream based mode results in dropped frames and corruption. Not often,
+ but occasionally. Many thanks go to Leonard Orb who spent a lot of
+ effort and time trying to trace the cause of the drop outs. */
+ /* 1 frame per DMA */
+ /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */
+ ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1);
+
+ /* Stuff from Windows, we don't know what it is */
+ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0);
+ /* According to the docs, this should be correct. However, this is
+ untested. I don't dare enable this without having tested it.
+ Only very few old cards actually have this hardware combination.
+ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1,
+ ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0);
+ */
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+
+ /* assign placeholder */
+ ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, itv->digitizer, itv->digitizer);
+
+ /* Setup VBI */
+ if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
+ ivtv_vbi_setup(itv);
+ }
+
+ /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */
+ ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400);
+ itv->pgm_info_offset = data[0];
+ itv->pgm_info_num = data[1];
+ itv->pgm_info_write_idx = 0;
+ itv->pgm_info_read_idx = 0;
+
+ IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n",
+ itv->pgm_info_offset, itv->pgm_info_num);
+
+ /* Setup API for Stream */
+ cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+ }
+
+ /* Vsync Setup */
+ if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+ /* event notification (on) */
+ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1);
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+ }
+
+ if (atomic_read(&itv->capturing) == 0) {
+ /* Clear all Pending Interrupts */
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+ clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+
+ /* Initialize Digitizer for Capture */
+ ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+ ivtv_sleep_timeout(HZ / 10, 0);
+ }
+
+ /* begin_capture */
+ if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype))
+ {
+ IVTV_DEBUG_WARN( "Error starting capture!\n");
+ return -EINVAL;
+ }
+
+ /* Start Passthrough */
+ if (enable_passthrough) {
+ ivtv_passthrough_mode(itv, 1);
+ }
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+ else
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+ /* you're live! sit back and await interrupts :) */
+ atomic_inc(&itv->capturing);
+ return 0;
+}
+
+static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv *itv = s->itv;
+ int datatype;
+
+ if (s->v4l2dev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
+
+ /* disable VBI signals, if the MPEG stream contains VBI data,
+ then that data will be processed automatically for you. */
+ ivtv_disable_vbi(itv);
+
+ /* set audio mode to left/stereo for dual/stereo mode. */
+ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+
+ /* set number of internal decoder buffers */
+ ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0);
+
+ /* prebuffering */
+ ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1);
+
+ /* extract from user packets */
+ ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1);
+ itv->vbi.dec_start = data[0];
+
+ IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n",
+ itv->vbi.dec_start, data[1]);
+
+ /* set decoder source settings */
+ /* Data type: 0 = mpeg from host,
+ 1 = yuv from encoder,
+ 2 = yuv_from_host */
+ switch (s->type) {
+ case IVTV_DEC_STREAM_TYPE_YUV:
+ datatype = itv->output_mode == OUT_PASSTHROUGH ? 1 : 2;
+ IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype);
+ break;
+ case IVTV_DEC_STREAM_TYPE_MPG:
+ default:
+ datatype = 0;
+ break;
+ }
+ if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
+ itv->params.width, itv->params.height, itv->params.audio_properties)) {
+ IVTV_DEBUG_WARN("COULDN'T INITIALIZE DECODER SOURCE\n");
+ }
+ return 0;
+}
+
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
+{
+ struct ivtv *itv = s->itv;
+
+ if (s->v4l2dev == NULL)
+ return -EINVAL;
+
+ if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
+ return 0; /* already started */
+
+ IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
+
+ /* Clear Streamoff */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ /* Initialize Decoder */
+ /* Reprogram Decoder YUV Buffers for YUV */
+ write_reg(yuv_offset[0] >> 4, 0x82c);
+ write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+ write_reg(yuv_offset[0] >> 4, 0x834);
+ write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+
+ write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24);
+
+ write_reg_sync(0x00108080, 0x2898);
+ /* Enable YUV decoder output */
+ write_reg_sync(0x01, IVTV_REG_VDM);
+ }
+
+ ivtv_setup_v4l2_decode_stream(s);
+
+ /* set dma size to 65536 bytes */
+ ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
+
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ /* Zero out decoder counters */
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]);
+
+ /* turn on notification of dual/stereo mode change */
+ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+ /* start playback */
+ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0);
+
+ /* Clear the following Interrupt mask bits for decoding */
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+ IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask);
+
+ /* you're live! sit back and await interrupts :) */
+ atomic_inc(&itv->decoding);
+ return 0;
+}
+
+void ivtv_stop_all_captures(struct ivtv *itv)
+{
+ int i;
+
+ for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
+ struct ivtv_stream *s = &itv->streams[i];
+
+ if (s->v4l2dev == NULL)
+ continue;
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ ivtv_stop_v4l2_encode_stream(s, 0);
+ }
+ }
+}
+
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
+{
+ struct ivtv *itv = s->itv;
+ DECLARE_WAITQUEUE(wait, current);
+ int cap_type;
+ unsigned long then;
+ int stopmode;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ if (s->v4l2dev == NULL)
+ return -EINVAL;
+
+ /* This function assumes that you are allowed to stop the capture
+ and that we are actually capturing */
+
+ IVTV_DEBUG_INFO("Stop Capture\n");
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_VOUT)
+ return 0;
+ if (atomic_read(&itv->capturing) == 0)
+ return 0;
+
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ default:
+ cap_type = 0;
+ break;
+ }
+
+ /* Stop Capture Mode */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+ stopmode = 0;
+ } else {
+ stopmode = 1;
+ }
+
+ /* end_capture */
+ /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
+ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
+
+ /* only run these if we're shutting down the last cap */
+ if (atomic_read(&itv->capturing) - 1 == 0) {
+ /* event notification (off) */
+ if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+ /* type: 0 = refresh */
+ /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */
+ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1);
+ ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+ }
+ }
+
+ then = jiffies;
+
+ if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+ /* only run these if we're shutting down the last cap */
+ unsigned long duration;
+
+ then = jiffies;
+ add_wait_queue(&itv->cap_w, &wait);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* wait 2s for EOS interrupt */
+ while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && jiffies < then + 2 * HZ) {
+ schedule_timeout(HZ / 100);
+ }
+
+ /* To convert jiffies to ms, we must multiply by 1000
+ * and divide by HZ. To avoid runtime division, we
+ * convert this to multiplication by 1000/HZ.
+ * Since integer division truncates, we get the best
+ * accuracy if we do a rounding calculation of the constant.
+ * Think of the case where HZ is 1024.
+ */
+ duration = ((1000 + HZ / 2) / HZ) * (jiffies - then);
+
+ if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
+ IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name);
+ IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration);
+ } else {
+ IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&itv->cap_w, &wait);
+ }
+
+ then = jiffies;
+ /* Make sure DMA is complete */
+ add_wait_queue(&s->waitq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ do {
+ /* check if DMA is pending */
+ if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) && /* MPG Only */
+ (read_reg(IVTV_REG_DMASTATUS) & 0x02)) {
+ /* Check for last DMA */
+ ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0);
+
+ if (data[0] == 1) {
+ IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]);
+ break;
+ }
+ } else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) {
+ break;
+ }
+
+ ivtv_sleep_timeout(HZ / 100, 1);
+ } while (then + HZ * 2 > jiffies);
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&s->waitq, &wait);
+ }
+
+ atomic_dec(&itv->capturing);
+
+ /* Clear capture and no-read bits */
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+
+ if (atomic_read(&itv->capturing) > 0) {
+ return 0;
+ }
+
+ /* Set the following Interrupt mask bits for capture */
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+ wake_up(&s->waitq);
+
+ return 0;
+}
+
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
+{
+ struct ivtv *itv = s->itv;
+
+ if (s->v4l2dev == NULL)
+ return -EINVAL;
+
+ if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
+ return -EINVAL;
+
+ if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags))
+ return 0;
+
+ IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", pts, flags);
+
+ /* Stop Decoder */
+ if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) {
+ u32 tmp = 0;
+
+ /* Wait until the decoder is no longer running */
+ if (pts) {
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3,
+ 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32));
+ }
+ while (1) {
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0);
+ if (s->q_full.buffers + s->q_dma.buffers == 0) {
+ if (tmp == data[3])
+ break;
+ tmp = data[3];
+ }
+ if (ivtv_sleep_timeout(HZ/10, 1))
+ break;
+ }
+ }
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0);
+
+ /* turn off notification of dual/stereo mode change */
+ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ ivtv_flush_queues(s);
+
+ if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+ /* disable VBI on TV-out */
+ ivtv_disable_vbi(itv);
+ }
+
+ /* decrement decoding */
+ atomic_dec(&itv->decoding);
+
+ set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
+ wake_up(&itv->event_waitq);
+
+ /* wake up wait queues */
+ wake_up(&s->waitq);
+
+ return 0;
+}
+
+int ivtv_passthrough_mode(struct ivtv *itv, int enable)
+{
+ struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
+ struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+
+ if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
+
+ /* Prevent others from starting/stopping streams while we
+ initiate/terminate passthrough mode */
+ if (enable) {
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ return 0;
+ }
+ if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH)
+ return -EBUSY;
+
+ /* Fully initialize stream, and then unflag init */
+ set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+ set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+
+ /* Setup YUV Decoder */
+ ivtv_setup_v4l2_decode_stream(dec_stream);
+
+ /* Start Decoder */
+ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1);
+ atomic_inc(&itv->decoding);
+
+ /* Setup capture if not already done */
+ if (atomic_read(&itv->capturing) == 0) {
+ cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+ }
+
+ /* Start Passthrough Mode */
+ ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11);
+ atomic_inc(&itv->capturing);
+ return 0;
+ }
+
+ if (itv->output_mode != OUT_PASSTHROUGH)
+ return 0;
+
+ /* Stop Passthrough Mode */
+ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11);
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0);
+
+ atomic_dec(&itv->capturing);
+ atomic_dec(&itv->decoding);
+ clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+ itv->output_mode = OUT_NONE;
+
+ return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/video/ivtv/ivtv-streams.h
new file mode 100644
index 000000000000..8597b75384a7
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-streams.h
@@ -0,0 +1,31 @@
+/*
+ init/start/stop/exit stream functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+int ivtv_streams_setup(struct ivtv *itv);
+void ivtv_streams_cleanup(struct ivtv *itv);
+
+/* Capture related */
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s);
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end);
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset);
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts);
+
+void ivtv_stop_all_captures(struct ivtv *itv);
+int ivtv_passthrough_mode(struct ivtv *itv, int enable);
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
new file mode 100644
index 000000000000..bd642e1aafc3
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-udma.c
@@ -0,0 +1,200 @@
+/*
+ User DMA
+
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-streams.h"
+#include "ivtv-udma.h"
+
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size)
+{
+ dma_page->uaddr = first & PAGE_MASK;
+ dma_page->offset = first & ~PAGE_MASK;
+ dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK);
+ dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT;
+ dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT;
+ dma_page->page_count = dma_page->last - dma_page->first + 1;
+ if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset;
+}
+
+int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset)
+{
+ int i, offset;
+
+ offset = dma_page->offset;
+
+ /* Fill SG Array with new values */
+ for (i = 0; i < dma_page->page_count; i++) {
+ if (i == dma_page->page_count - 1) {
+ dma->SGlist[map_offset].length = dma_page->tail;
+ }
+ else {
+ dma->SGlist[map_offset].length = PAGE_SIZE - offset;
+ }
+ dma->SGlist[map_offset].offset = offset;
+ dma->SGlist[map_offset].page = dma->map[map_offset];
+ offset = 0;
+ map_offset++;
+ }
+ return map_offset;
+}
+
+void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) {
+ int i;
+ struct scatterlist *sg;
+
+ for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) {
+ dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg));
+ dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg));
+ dma->SGarray[i].dst = cpu_to_le32(buffer_offset);
+ buffer_offset += sg_dma_len(sg);
+
+ split -= sg_dma_len(sg);
+ if (split == 0)
+ buffer_offset = buffer_offset_2;
+ }
+}
+
+/* User DMA Buffers */
+void ivtv_udma_alloc(struct ivtv *itv)
+{
+ if (itv->udma.SG_handle == 0) {
+ /* Map DMA Page Array Buffer */
+ itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+ ivtv_udma_sync_for_cpu(itv);
+ }
+}
+
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+ void __user *userbuf, int size_in_bytes)
+{
+ struct ivtv_dma_page_info user_dma;
+ struct ivtv_user_dma *dma = &itv->udma;
+ int err;
+
+ IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr);
+
+ /* Still in USE */
+ if (dma->SG_length || dma->page_count) {
+ IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n",
+ dma->SG_length, dma->page_count);
+ return -EBUSY;
+ }
+
+ ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes);
+
+ if (user_dma.page_count <= 0) {
+ IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n",
+ user_dma.page_count, size_in_bytes, user_dma.offset);
+ return -EINVAL;
+ }
+
+ /* Get user pages for DMA Xfer */
+ down_read(&current->mm->mmap_sem);
+ err = get_user_pages(current, current->mm,
+ user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (user_dma.page_count != err) {
+ IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
+ err, user_dma.page_count);
+ return -EINVAL;
+ }
+
+ dma->page_count = user_dma.page_count;
+
+ /* Fill SG List with new values */
+ ivtv_udma_fill_sg_list(dma, &user_dma, 0);
+
+ /* Map SG List */
+ dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+ /* Fill SG Array with new values */
+ ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1);
+
+ /* Tag SG Array with Interrupt Bit */
+ dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+ ivtv_udma_sync_for_device(itv);
+ return dma->page_count;
+}
+
+void ivtv_udma_unmap(struct ivtv *itv)
+{
+ struct ivtv_user_dma *dma = &itv->udma;
+ int i;
+
+ IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n");
+
+ /* Nothing to free */
+ if (dma->page_count == 0)
+ return;
+
+ /* Unmap Scatterlist */
+ if (dma->SG_length) {
+ pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+ dma->SG_length = 0;
+ }
+ /* sync DMA */
+ ivtv_udma_sync_for_cpu(itv);
+
+ /* Release User Pages */
+ for (i = 0; i < dma->page_count; i++) {
+ put_page(dma->map[i]);
+ }
+ dma->page_count = 0;
+}
+
+void ivtv_udma_free(struct ivtv *itv)
+{
+ /* Unmap SG Array */
+ if (itv->udma.SG_handle) {
+ pci_unmap_single(itv->dev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+ }
+
+ /* Unmap Scatterlist */
+ if (itv->udma.SG_length) {
+ pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
+ }
+}
+
+void ivtv_udma_start(struct ivtv *itv)
+{
+ IVTV_DEBUG_DMA("start UDMA\n");
+ write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR);
+ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+ set_bit(IVTV_F_I_DMA, &itv->i_flags);
+ set_bit(IVTV_F_I_UDMA, &itv->i_flags);
+}
+
+void ivtv_udma_prepare(struct ivtv *itv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&itv->dma_reg_lock, flags);
+ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+ ivtv_udma_start(itv);
+ else
+ set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
+ spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h
new file mode 100644
index 000000000000..e131bccedec0
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-udma.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2006-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+/* User DMA functions */
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size);
+int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset);
+void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split);
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+ void __user *userbuf, int size_in_bytes);
+void ivtv_udma_unmap(struct ivtv *itv);
+void ivtv_udma_free(struct ivtv *itv);
+void ivtv_udma_alloc(struct ivtv *itv);
+void ivtv_udma_prepare(struct ivtv *itv);
+void ivtv_udma_start(struct ivtv *itv);
+
+static inline void ivtv_udma_sync_for_device(struct ivtv *itv)
+{
+ pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv)
+{
+ pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
new file mode 100644
index 000000000000..5efa5a867818
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-vbi.c
@@ -0,0 +1,538 @@
+/*
+ Vertical Blank Interval support functions
+ Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-video.h"
+#include "ivtv-vbi.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-queue.h"
+
+static int odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static void passthrough_vbi_data(struct ivtv *itv, int cnt)
+{
+ int wss = 0;
+ u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 };
+ u8 vps[13];
+ int found_cc = 0;
+ int found_wss = 0;
+ int found_vps = 0;
+ int cc_pos = itv->vbi.cc_pos;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ struct v4l2_sliced_vbi_data *d = itv->vbi.sliced_dec_data + i;
+
+ if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) {
+ found_cc = 1;
+ if (d->field) {
+ cc[2] = d->data[0];
+ cc[3] = d->data[1];
+ } else {
+ cc[0] = d->data[0];
+ cc[1] = d->data[1];
+ }
+ }
+ else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) {
+ memcpy(vps, d->data, sizeof(vps));
+ found_vps = 1;
+ }
+ else if (d->id == V4L2_SLICED_WSS_625 && d->line == 23 && d->field == 0) {
+ wss = d->data[0] | d->data[1] << 8;
+ found_wss = 1;
+ }
+ }
+
+ if (itv->vbi.wss_found != found_wss || itv->vbi.wss != wss) {
+ itv->vbi.wss = wss;
+ itv->vbi.wss_found = found_wss;
+ set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+ }
+
+ if (found_vps || itv->vbi.vps_found) {
+ itv->vbi.vps[0] = vps[2];
+ itv->vbi.vps[1] = vps[8];
+ itv->vbi.vps[2] = vps[9];
+ itv->vbi.vps[3] = vps[10];
+ itv->vbi.vps[4] = vps[11];
+ itv->vbi.vps_found = found_vps;
+ set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+ }
+
+ if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) {
+ itv->vbi.cc_data_odd[cc_pos] = cc[0];
+ itv->vbi.cc_data_odd[cc_pos + 1] = cc[1];
+ itv->vbi.cc_data_even[cc_pos] = cc[2];
+ itv->vbi.cc_data_even[cc_pos + 1] = cc[3];
+ itv->vbi.cc_pos = cc_pos + 2;
+ set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+ }
+}
+
+static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp)
+{
+ int line = 0;
+ int i;
+ u32 linemask[2] = { 0, 0 };
+ unsigned short size;
+ static const u8 mpeg_hdr_data[] = {
+ 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66,
+ 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff,
+ 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80,
+ 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff
+ };
+ const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */
+ int idx = itv->vbi.frame % IVTV_VBI_FRAMES;
+ u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0];
+
+ for (i = 0; i < lines; i++) {
+ int f, l;
+
+ if (itv->vbi.sliced_data[i].id == 0)
+ continue;
+
+ l = itv->vbi.sliced_data[i].line - 6;
+ f = itv->vbi.sliced_data[i].field;
+ if (f)
+ l += 18;
+ if (l < 32)
+ linemask[0] |= (1 << l);
+ else
+ linemask[1] |= (1 << (l - 32));
+ dst[sd + 12 + line * 43] = service2vbi(itv->vbi.sliced_data[i].id);
+ memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42);
+ line++;
+ }
+ memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
+ if (line == 36) {
+ /* All lines are used, so there is no space for the linemask
+ (the max size of the VBI data is 36 * 43 + 4 bytes).
+ So in this case we use the magic number 'ITV0'. */
+ memcpy(dst + sd, "ITV0", 4);
+ memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+ size = 4 + ((43 * line + 3) & ~3);
+ } else {
+ memcpy(dst + sd, "itv0", 4);
+ memcpy(dst + sd + 4, &linemask[0], 8);
+ size = 12 + ((43 * line + 3) & ~3);
+ }
+ dst[4+16] = (size + 10) >> 8;
+ dst[5+16] = (size + 10) & 0xff;
+ dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
+ dst[10+16] = (pts_stamp >> 22) & 0xff;
+ dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
+ dst[12+16] = (pts_stamp >> 7) & 0xff;
+ dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
+ itv->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p)
+{
+ u32 linemask[2];
+ int i, l, id2;
+ int line = 0;
+
+ if (!memcmp(p, "itv0", 4)) {
+ memcpy(linemask, p + 4, 8);
+ p += 12;
+ } else if (!memcmp(p, "ITV0", 4)) {
+ linemask[0] = 0xffffffff;
+ linemask[1] = 0xf;
+ p += 4;
+ } else {
+ /* unknown VBI data stream */
+ return 0;
+ }
+ for (i = 0; i < 36; i++) {
+ int err = 0;
+
+ if (i < 32 && !(linemask[0] & (1 << i)))
+ continue;
+ if (i >= 32 && !(linemask[1] & (1 << (i - 32))))
+ continue;
+ id2 = *p & 0xf;
+ switch (id2) {
+ case IVTV_SLICED_TYPE_TELETEXT_B:
+ id2 = V4L2_SLICED_TELETEXT_B;
+ break;
+ case IVTV_SLICED_TYPE_CAPTION_525:
+ id2 = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[1]) || !odd_parity(p[2]);
+ break;
+ case IVTV_SLICED_TYPE_VPS:
+ id2 = V4L2_SLICED_VPS;
+ break;
+ case IVTV_SLICED_TYPE_WSS_625:
+ id2 = V4L2_SLICED_WSS_625;
+ break;
+ default:
+ id2 = 0;
+ break;
+ }
+ if (err == 0) {
+ l = (i < 18) ? i + 6 : i - 18 + 6;
+ itv->vbi.sliced_dec_data[line].line = l;
+ itv->vbi.sliced_dec_data[line].field = i >= 18;
+ itv->vbi.sliced_dec_data[line].id = id2;
+ memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42);
+ line++;
+ }
+ p += 43;
+ }
+ while (line < 36) {
+ itv->vbi.sliced_dec_data[line].id = 0;
+ itv->vbi.sliced_dec_data[line].line = 0;
+ itv->vbi.sliced_dec_data[line].field = 0;
+ line++;
+ }
+ return line * sizeof(itv->vbi.sliced_dec_data[0]);
+}
+
+ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count)
+{
+ /* Should be a __user pointer, but sparse doesn't parse this bit correctly. */
+ const struct v4l2_sliced_vbi_data *p = (const struct v4l2_sliced_vbi_data *)ubuf;
+ u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 };
+ int found_cc = 0;
+ int cc_pos = itv->vbi.cc_pos;
+
+ if (itv->vbi.service_set_out == 0)
+ return -EPERM;
+
+ while (count >= sizeof(struct v4l2_sliced_vbi_data)) {
+ switch (p->id) {
+ case V4L2_SLICED_CAPTION_525:
+ if (p->id == V4L2_SLICED_CAPTION_525 &&
+ p->line == 21 &&
+ (itv->vbi.service_set_out &
+ V4L2_SLICED_CAPTION_525) == 0) {
+ break;
+ }
+ found_cc = 1;
+ if (p->field) {
+ cc[2] = p->data[0];
+ cc[3] = p->data[1];
+ } else {
+ cc[0] = p->data[0];
+ cc[1] = p->data[1];
+ }
+ break;
+
+ case V4L2_SLICED_VPS:
+ if (p->line == 16 && p->field == 0 &&
+ (itv->vbi.service_set_out & V4L2_SLICED_VPS)) {
+ itv->vbi.vps[0] = p->data[2];
+ itv->vbi.vps[1] = p->data[8];
+ itv->vbi.vps[2] = p->data[9];
+ itv->vbi.vps[3] = p->data[10];
+ itv->vbi.vps[4] = p->data[11];
+ itv->vbi.vps_found = 1;
+ set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+ }
+ break;
+
+ case V4L2_SLICED_WSS_625:
+ if (p->line == 23 && p->field == 0 &&
+ (itv->vbi.service_set_out & V4L2_SLICED_WSS_625)) {
+ /* No lock needed for WSS */
+ itv->vbi.wss = p->data[0] | (p->data[1] << 8);
+ itv->vbi.wss_found = 1;
+ set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+ }
+ break;
+
+ default:
+ break;
+ }
+ count -= sizeof(*p);
+ p++;
+ }
+
+ if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) {
+ itv->vbi.cc_data_odd[cc_pos] = cc[0];
+ itv->vbi.cc_data_odd[cc_pos + 1] = cc[1];
+ itv->vbi.cc_data_even[cc_pos] = cc[2];
+ itv->vbi.cc_data_even[cc_pos + 1] = cc[3];
+ itv->vbi.cc_pos = cc_pos + 2;
+ set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+ }
+
+ return (const char __user *)p - ubuf;
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space after the
+ field.
+ Returns new compressed size. */
+static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size)
+{
+ u32 line_size = itv->vbi.raw_decoder_line_size;
+ u32 lines = itv->vbi.count;
+ u8 sav1 = itv->vbi.raw_decoder_sav_odd_field;
+ u8 sav2 = itv->vbi.raw_decoder_sav_even_field;
+ u8 *q = buf;
+ u8 *p;
+ int i;
+
+ for (i = 0; i < lines; i++) {
+ p = buf + i * line_size;
+
+ /* Look for SAV code */
+ if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) {
+ break;
+ }
+ memcpy(q, p + 4, line_size - 4);
+ q += line_size - 4;
+ }
+ return lines * (line_size - 4);
+}
+
+
+/* Compressed VBI format, all found sliced blocks put next to one another
+ Returns new compressed size */
+static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav)
+{
+ u32 line_size = itv->vbi.sliced_decoder_line_size;
+ struct v4l2_decode_vbi_line vbi;
+ int i;
+
+ /* find the first valid line */
+ for (i = 0; i < size; i++, buf++) {
+ if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
+ break;
+ }
+
+ size -= i;
+ if (size < line_size) {
+ return line;
+ }
+ for (i = 0; i < size / line_size; i++) {
+ u8 *p = buf + i * line_size;
+
+ /* Look for SAV code */
+ if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) {
+ continue;
+ }
+ vbi.p = p + 4;
+ itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+ if (vbi.type) {
+ itv->vbi.sliced_data[line].id = vbi.type;
+ itv->vbi.sliced_data[line].field = vbi.is_second_field;
+ itv->vbi.sliced_data[line].line = vbi.line;
+ memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42);
+ line++;
+ }
+ }
+ return line;
+}
+
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+ u64 pts_stamp, int streamtype)
+{
+ u8 *p = (u8 *) buf->buf;
+ u32 size = buf->bytesused;
+ int y;
+
+ /* Raw VBI data */
+ if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set == 0) {
+ u8 type;
+
+ ivtv_buf_swap(buf);
+
+ type = p[3];
+
+ size = buf->bytesused = compress_raw_buf(itv, p, size);
+
+ /* second field of the frame? */
+ if (type == itv->vbi.raw_decoder_sav_even_field) {
+ /* Dirty hack needed for backwards
+ compatibility of old VBI software. */
+ p += size - 4;
+ memcpy(p, &itv->vbi.frame, 4);
+ itv->vbi.frame++;
+ }
+ return;
+ }
+
+ /* Sliced VBI data with data insertion */
+ if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) {
+ int lines;
+
+ ivtv_buf_swap(buf);
+
+ /* first field */
+ lines = compress_sliced_buf(itv, 0, p, size / 2,
+ itv->vbi.sliced_decoder_sav_odd_field);
+ /* second field */
+ /* experimentation shows that the second half does not always begin
+ at the exact address. So start a bit earlier (hence 32). */
+ lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32,
+ itv->vbi.sliced_decoder_sav_even_field);
+ /* always return at least one empty line */
+ if (lines == 0) {
+ itv->vbi.sliced_data[0].id = 0;
+ itv->vbi.sliced_data[0].line = 0;
+ itv->vbi.sliced_data[0].field = 0;
+ lines = 1;
+ }
+ buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]);
+ memcpy(p, &itv->vbi.sliced_data[0], size);
+
+ if (itv->vbi.insert_mpeg) {
+ copy_vbi_data(itv, lines, pts_stamp);
+ }
+ itv->vbi.frame++;
+ return;
+ }
+
+ /* Sliced VBI re-inserted from an MPEG stream */
+ if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
+ /* If the size is not 4-byte aligned, then the starting address
+ for the swapping is also shifted. After swapping the data the
+ real start address of the VBI data is exactly 4 bytes after the
+ original start. It's a bit fiddly but it works like a charm.
+ Non-4-byte alignment happens when an lseek is done on the input
+ mpeg file to a non-4-byte aligned position. So on arrival here
+ the VBI data is also non-4-byte aligned. */
+ int offset = size & 3;
+ int cnt;
+
+ if (offset) {
+ p += 4 - offset;
+ }
+ /* Swap Buffer */
+ for (y = 0; y < size; y += 4) {
+ swab32s((u32 *)(p + y));
+ }
+
+ cnt = ivtv_convert_ivtv_vbi(itv, p + offset);
+ memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt);
+ buf->bytesused = cnt;
+
+ passthrough_vbi_data(itv, cnt / sizeof(itv->vbi.sliced_dec_data[0]));
+ return;
+ }
+}
+
+void ivtv_disable_vbi(struct ivtv *itv)
+{
+ clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+ clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+ clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+ ivtv_set_wss(itv, 0, 0);
+ ivtv_set_cc(itv, 0, 0, 0, 0, 0);
+ ivtv_set_vps(itv, 0, 0, 0, 0, 0, 0);
+ itv->vbi.vps_found = itv->vbi.wss_found = 0;
+ itv->vbi.wss = 0;
+ itv->vbi.cc_pos = 0;
+}
+
+
+void vbi_work_handler(struct ivtv *itv)
+{
+ struct v4l2_sliced_vbi_data data;
+
+ /* Lock */
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ /* Note: currently only the saa7115 is used in a PVR350,
+ so these commands are for now saa7115 specific. */
+ if (itv->is_50hz) {
+ data.id = V4L2_SLICED_WSS_625;
+ data.field = 0;
+
+ if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ ivtv_set_wss(itv, 1, data.data[0] & 0xf);
+ itv->vbi.wss_no_update = 0;
+ } else if (itv->vbi.wss_no_update == 4) {
+ ivtv_set_wss(itv, 1, 0x8); /* 4x3 full format */
+ } else {
+ itv->vbi.wss_no_update++;
+ }
+ }
+ else {
+ u8 c1 = 0, c2 = 0, c3 = 0, c4 = 0;
+ int mode = 0;
+
+ data.id = V4L2_SLICED_CAPTION_525;
+ data.field = 0;
+ if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ mode |= 1;
+ c1 = data.data[0];
+ c2 = data.data[1];
+ }
+ data.field = 1;
+ if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ mode |= 2;
+ c3 = data.data[0];
+ c4 = data.data[1];
+ }
+ if (mode) {
+ itv->vbi.cc_no_update = 0;
+ ivtv_set_cc(itv, mode, c1, c2, c3, c4);
+ } else if (itv->vbi.cc_no_update == 4) {
+ ivtv_set_cc(itv, 0, 0, 0, 0, 0);
+ } else {
+ itv->vbi.cc_no_update++;
+ }
+ }
+ return;
+ }
+
+ if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) {
+ /* Lock */
+ ivtv_set_wss(itv, itv->vbi.wss_found, itv->vbi.wss & 0xf);
+ }
+
+ if (test_and_clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) {
+ if (itv->vbi.cc_pos == 0) {
+ ivtv_set_cc(itv, 3, 0x80, 0x80, 0x80, 0x80);
+ }
+ while (itv->vbi.cc_pos) {
+ u8 cc_odd0 = itv->vbi.cc_data_odd[0];
+ u8 cc_odd1 = itv->vbi.cc_data_odd[1];
+ u8 cc_even0 = itv->vbi.cc_data_even[0];
+ u8 cc_even1 = itv->vbi.cc_data_even[1];
+
+ memcpy(itv->vbi.cc_data_odd, itv->vbi.cc_data_odd + 2, sizeof(itv->vbi.cc_data_odd) - 2);
+ memcpy(itv->vbi.cc_data_even, itv->vbi.cc_data_even + 2, sizeof(itv->vbi.cc_data_even) - 2);
+ itv->vbi.cc_pos -= 2;
+ if (itv->vbi.cc_pos && cc_odd0 == 0x80 && cc_odd1 == 0x80)
+ continue;
+
+ /* Send to Saa7127 */
+ ivtv_set_cc(itv, 3, cc_odd0, cc_odd1, cc_even0, cc_even1);
+ if (itv->vbi.cc_pos == 0)
+ set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+ break;
+ }
+ }
+
+ if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) {
+ /* Lock */
+ ivtv_set_vps(itv, itv->vbi.vps_found,
+ itv->vbi.vps[0], itv->vbi.vps[1],
+ itv->vbi.vps[2], itv->vbi.vps[3], itv->vbi.vps[4]);
+ }
+}
diff --git a/drivers/media/video/ivtv/ivtv-vbi.h b/drivers/media/video/ivtv/ivtv-vbi.h
new file mode 100644
index 000000000000..cdaea697b3ec
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-vbi.h
@@ -0,0 +1,26 @@
+/*
+ Vertical Blank Interval support functions
+ Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count);
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+ u64 pts_stamp, int streamtype);
+int ivtv_used_line(struct ivtv *itv, int line, int field);
+void ivtv_disable_vbi(struct ivtv *itv);
+void ivtv_set_vbi(unsigned long arg);
+void vbi_work_handler(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
new file mode 100644
index 000000000000..85530a3cd369
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-version.h
@@ -0,0 +1,26 @@
+/*
+ ivtv driver version information
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 IVTV_DRIVER_NAME "ivtv"
+#define IVTV_DRIVER_VERSION_MAJOR 1
+#define IVTV_DRIVER_VERSION_MINOR 0
+#define IVTV_DRIVER_VERSION_PATCHLEVEL 0
+
+#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
+#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)
diff --git a/drivers/media/video/ivtv/ivtv-video.c b/drivers/media/video/ivtv/ivtv-video.c
new file mode 100644
index 000000000000..5858b197d510
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-video.c
@@ -0,0 +1,142 @@
+/*
+ saa7127 interface functions
+ Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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 "ivtv-driver.h"
+#include "ivtv-video.h"
+#include "ivtv-i2c.h"
+#include "ivtv-gpio.h"
+#include "ivtv-cards.h"
+#include <media/upd64031a.h>
+#include <media/upd64083.h>
+
+void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3,
+ u8 vps4, u8 vps5)
+{
+ struct v4l2_sliced_vbi_data data;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return;
+ data.id = V4L2_SLICED_VPS;
+ data.field = 0;
+ data.line = enabled ? 16 : 0;
+ data.data[4] = vps1;
+ data.data[10] = vps2;
+ data.data[11] = vps3;
+ data.data[12] = vps4;
+ data.data[13] = vps5;
+ ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4)
+{
+ struct v4l2_sliced_vbi_data data;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return;
+ data.id = V4L2_SLICED_CAPTION_525;
+ data.field = 0;
+ data.line = (mode & 1) ? 21 : 0;
+ data.data[0] = cc1;
+ data.data[1] = cc2;
+ ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ data.field = 1;
+ data.line = (mode & 2) ? 21 : 0;
+ data.data[0] = cc3;
+ data.data[1] = cc4;
+ ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
+{
+ struct v4l2_sliced_vbi_data data;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return;
+ /* When using a 50 Hz system, always turn on the
+ wide screen signal with 4x3 ratio as the default.
+ Turning this signal on and off can confuse certain
+ TVs. As far as I can tell there is no reason not to
+ transmit this signal. */
+ if ((itv->std & V4L2_STD_625_50) && !enabled) {
+ enabled = 1;
+ mode = 0x08; /* 4x3 full format */
+ }
+ data.id = V4L2_SLICED_WSS_625;
+ data.field = 0;
+ data.line = enabled ? 23 : 0;
+ data.data[0] = mode & 0xff;
+ data.data[1] = (mode >> 8) & 0xff;
+ ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_video_set_io(struct ivtv *itv)
+{
+ struct v4l2_routing route;
+ int inp = itv->active_input;
+ u32 type;
+
+ route.input = itv->card->video_inputs[inp].video_input;
+ route.output = 0;
+ itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+ type = itv->card->video_inputs[inp].video_type;
+
+ if (type == IVTV_CARD_INPUT_VID_TUNER) {
+ route.input = 0; /* Tuner */
+ } else if (type < IVTV_CARD_INPUT_COMPOSITE1) {
+ route.input = 2; /* S-Video */
+ } else {
+ route.input = 1; /* Composite */
+ }
+
+ if (itv->card->hw_video & IVTV_HW_GPIO)
+ ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+ if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+ if (type == IVTV_CARD_INPUT_VID_TUNER ||
+ type >= IVTV_CARD_INPUT_COMPOSITE1) {
+ /* Composite: GR on, connect to 3DYCS */
+ route.input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE;
+ } else {
+ /* S-Video: GR bypassed, turn it off */
+ route.input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE;
+ }
+ route.input |= itv->card->gr_config;
+
+ ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ }
+
+ if (itv->card->hw_video & IVTV_HW_UPD6408X) {
+ route.input = UPD64083_YCS_MODE;
+ if (type > IVTV_CARD_INPUT_VID_TUNER &&
+ type < IVTV_CARD_INPUT_COMPOSITE1) {
+ /* S-Video uses YCNR mode and internal Y-ADC, the upd64031a
+ is not used. */
+ route.input |= UPD64083_YCNR_MODE;
+ }
+ else if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+ /* Use upd64031a output for tuner and composite(CX23416GYC only) inputs */
+ if ((type == IVTV_CARD_INPUT_VID_TUNER)||
+ (itv->card->type == IVTV_CARD_CX23416GYC)) {
+ route.input |= UPD64083_EXT_Y_ADC;
+ }
+ }
+ ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ }
+}
diff --git a/drivers/media/video/ivtv/ivtv-video.h b/drivers/media/video/ivtv/ivtv-video.h
new file mode 100644
index 000000000000..c8ade5d3c413
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-video.h
@@ -0,0 +1,24 @@
+/*
+ saa7127 interface functions
+ Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+void ivtv_set_wss(struct ivtv *itv, int enabled, int mode);
+void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4);
+void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3,
+ u8 vps4, u8 vps5);
+void ivtv_video_set_io(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
new file mode 100644
index 000000000000..bcea09542e5a
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -0,0 +1,1129 @@
+/*
+ yuv support
+
+ Copyright (C) 2007 Ian Armstrong <ian@iarmst.demon.co.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 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 "ivtv-driver.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-yuv.h"
+
+static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
+ struct ivtv_dma_frame *args)
+{
+ struct ivtv_dma_page_info y_dma;
+ struct ivtv_dma_page_info uv_dma;
+
+ int i;
+ int y_pages, uv_pages;
+
+ unsigned long y_buffer_offset, uv_buffer_offset;
+ int y_decode_height, uv_decode_height, y_size;
+ int frame = atomic_read(&itv->yuv_info.next_fill_frame);
+
+ y_buffer_offset = IVTV_DEC_MEM_START + yuv_offset[frame];
+ uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET;
+
+ y_decode_height = uv_decode_height = args->src.height + args->src.top;
+
+ if (y_decode_height < 512-16)
+ y_buffer_offset += 720 * 16;
+
+ if (y_decode_height & 15)
+ y_decode_height = (y_decode_height + 16) & ~15;
+
+ if (uv_decode_height & 31)
+ uv_decode_height = (uv_decode_height + 32) & ~31;
+
+ y_size = 720 * y_decode_height;
+
+ /* Still in USE */
+ if (dma->SG_length || dma->page_count) {
+ IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n",
+ dma->SG_length, dma->page_count);
+ return -EBUSY;
+ }
+
+ ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height);
+ ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height);
+
+ /* Get user pages for DMA Xfer */
+ down_read(&current->mm->mmap_sem);
+ y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL);
+ uv_pages = get_user_pages(current, current->mm, uv_dma.uaddr, uv_dma.page_count, 0, 1, &dma->map[y_pages], NULL);
+ up_read(&current->mm->mmap_sem);
+
+ dma->page_count = y_dma.page_count + uv_dma.page_count;
+
+ if (y_pages + uv_pages != dma->page_count) {
+ IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
+ y_pages + uv_pages, dma->page_count);
+
+ for (i = 0; i < dma->page_count; i++) {
+ put_page(dma->map[i]);
+ }
+ dma->page_count = 0;
+ return -EINVAL;
+ }
+
+ /* Fill & map SG List */
+ ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0));
+ dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+ /* Fill SG Array with new values */
+ ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size);
+
+ /* If we've offset the y plane, ensure top area is blanked */
+ if (args->src.height + args->src.top < 512-16) {
+ if (itv->yuv_info.blanking_dmaptr) {
+ dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
+ dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr);
+ dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DEC_MEM_START + yuv_offset[frame]);
+ dma->SG_length++;
+ }
+ }
+
+ /* Tag SG Array with Interrupt Bit */
+ dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+ ivtv_udma_sync_for_device(itv);
+ return 0;
+}
+
+/* We rely on a table held in the firmware - Quick check. */
+int ivtv_yuv_filter_check(struct ivtv *itv)
+{
+ int i, offset_y, offset_uv;
+
+ for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) {
+ if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) ||
+ (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) {
+ IVTV_WARN ("YUV filter table not found in firmware.\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
+{
+ int filter_index, filter_line;
+
+ /* If any filter is -1, then don't update it */
+ if (h_filter > -1) {
+ if (h_filter > 4) h_filter = 4;
+ filter_index = h_filter * 384;
+ filter_line = 0;
+ while (filter_line < 16) {
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804);
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c);
+ filter_index += 4;
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808);
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820);
+ filter_index += 4;
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c);
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824);
+ filter_index += 4;
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810);
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828);
+ filter_index += 4;
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814);
+ write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c);
+ filter_index += 8;
+ write_reg(0, 0x02818);
+ write_reg(0, 0x02830);
+ filter_line ++;
+ }
+ IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter);
+ }
+
+ if (v_filter_1 > -1) {
+ if (v_filter_1 > 4) v_filter_1 = 4;
+ filter_index = v_filter_1 * 192;
+ filter_line = 0;
+ while (filter_line < 16) {
+ write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900);
+ filter_index += 4;
+ write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904);
+ filter_index += 8;
+ write_reg(0, 0x02908);
+ filter_line ++;
+ }
+ IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1);
+ }
+
+ if (v_filter_2 > -1) {
+ if (v_filter_2 > 4) v_filter_2 = 4;
+ filter_index = v_filter_2 * 192;
+ filter_line = 0;
+ while (filter_line < 16) {
+ write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c);
+ filter_index += 4;
+ write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910);
+ filter_index += 8;
+ write_reg(0, 0x02914);
+ filter_line ++;
+ }
+ IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2);
+ }
+}
+
+static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window)
+{
+ u32 reg_2834, reg_2838, reg_283c;
+ u32 reg_2844, reg_2854, reg_285c;
+ u32 reg_2864, reg_2874, reg_2890;
+ u32 reg_2870, reg_2870_base, reg_2870_offset;
+ int x_cutoff;
+ int h_filter;
+ u32 master_width;
+
+ IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
+ window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x);
+
+ /* How wide is the src image */
+ x_cutoff = window->src_w + window->src_x;
+
+ /* Set the display width */
+ reg_2834 = window->dst_w;
+ reg_2838 = reg_2834;
+
+ /* Set the display position */
+ reg_2890 = window->dst_x;
+
+ /* Index into the image horizontally */
+ reg_2870 = 0;
+
+ /* 2870 is normally fudged to align video coords with osd coords.
+ If running full screen, it causes an unwanted left shift
+ Remove the fudge if we almost fill the screen.
+ Gradually adjust the offset to avoid the video 'snapping'
+ left/right if it gets dragged through this region.
+ Only do this if osd is full width. */
+ if (window->vis_w == 720) {
+ if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){
+ reg_2870 = 10 - (window->tru_x - window->pan_x) / 4;
+ }
+ else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) {
+ reg_2870 = (10 + (window->tru_x - window->pan_x) / 2);
+ }
+
+ if (window->dst_w >= window->src_w)
+ reg_2870 = reg_2870 << 16 | reg_2870;
+ else
+ reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1);
+ }
+
+ if (window->dst_w < window->src_w)
+ reg_2870 = 0x000d000e - reg_2870;
+ else
+ reg_2870 = 0x0012000e - reg_2870;
+
+ /* We're also using 2870 to shift the image left (src_x & negative dst_x) */
+ reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19;
+
+ if (window->dst_w >= window->src_w) {
+ x_cutoff &= ~1;
+ master_width = (window->src_w * 0x00200000) / (window->dst_w);
+ if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++;
+ reg_2834 = (reg_2834 << 16) | x_cutoff;
+ reg_2838 = (reg_2838 << 16) | x_cutoff;
+ reg_283c = master_width >> 2;
+ reg_2844 = master_width >> 2;
+ reg_2854 = master_width;
+ reg_285c = master_width >> 1;
+ reg_2864 = master_width >> 1;
+
+ /* We also need to factor in the scaling
+ (src_w - dst_w) / (src_w / 4) */
+ if (window->dst_w > window->src_w)
+ reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14);
+ else
+ reg_2870_base = 0;
+
+ reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base);
+ reg_2874 = 0;
+ }
+ else if (window->dst_w < window->src_w / 2) {
+ master_width = (window->src_w * 0x00080000) / window->dst_w;
+ if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++;
+ reg_2834 = (reg_2834 << 16) | x_cutoff;
+ reg_2838 = (reg_2838 << 16) | x_cutoff;
+ reg_283c = master_width >> 2;
+ reg_2844 = master_width >> 1;
+ reg_2854 = master_width;
+ reg_285c = master_width >> 1;
+ reg_2864 = master_width >> 1;
+ reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset);
+ reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16;
+ reg_2874 = 0x00000012;
+ }
+ else {
+ master_width = (window->src_w * 0x00100000) / window->dst_w;
+ if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++;
+ reg_2834 = (reg_2834 << 16) | x_cutoff;
+ reg_2838 = (reg_2838 << 16) | x_cutoff;
+ reg_283c = master_width >> 2;
+ reg_2844 = master_width >> 1;
+ reg_2854 = master_width;
+ reg_285c = master_width >> 1;
+ reg_2864 = master_width >> 1;
+ reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1);
+ reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16;
+ reg_2874 = 0x00000001;
+ }
+
+ /* Select the horizontal filter */
+ if (window->src_w == window->dst_w) {
+ /* An exact size match uses filter 0 */
+ h_filter = 0;
+ }
+ else {
+ /* Figure out which filter to use */
+ h_filter = ((window->src_w << 16) / window->dst_w) >> 15;
+ h_filter = (h_filter >> 1) + (h_filter & 1);
+ /* Only an exact size match can use filter 0 */
+ if (h_filter == 0) h_filter = 1;
+ }
+
+ write_reg(reg_2834, 0x02834);
+ write_reg(reg_2838, 0x02838);
+ IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838);
+
+ write_reg(reg_283c, 0x0283c);
+ write_reg(reg_2844, 0x02844);
+
+ IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844);
+
+ write_reg(0x00080514, 0x02840);
+ write_reg(0x00100514, 0x02848);
+ IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514);
+
+ write_reg(reg_2854, 0x02854);
+ IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854);
+
+ write_reg(reg_285c, 0x0285c);
+ write_reg(reg_2864, 0x02864);
+ IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864);
+
+ write_reg(reg_2874, 0x02874);
+ IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874);
+
+ write_reg(reg_2870, 0x02870);
+ IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870);
+
+ write_reg( reg_2890,0x02890);
+ IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890);
+
+ /* Only update the filter if we really need to */
+ if (h_filter != itv->yuv_info.h_filter) {
+ ivtv_yuv_filter (itv,h_filter,-1,-1);
+ itv->yuv_info.h_filter = h_filter;
+ }
+}
+
+static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window)
+{
+ u32 master_height;
+ u32 reg_2918, reg_291c, reg_2920, reg_2928;
+ u32 reg_2930, reg_2934, reg_293c;
+ u32 reg_2940, reg_2944, reg_294c;
+ u32 reg_2950, reg_2954, reg_2958, reg_295c;
+ u32 reg_2960, reg_2964, reg_2968, reg_296c;
+ u32 reg_289c;
+ u32 src_y_major_y, src_y_minor_y;
+ u32 src_y_major_uv, src_y_minor_uv;
+ u32 reg_2964_base, reg_2968_base;
+ int v_filter_1, v_filter_2;
+
+ IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
+ window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y);
+
+ /* What scaling mode is being used... */
+ if (window->interlaced_y) {
+ IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n");
+ }
+ else {
+ IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n");
+ }
+
+ if (window->interlaced_uv) {
+ IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n");
+ }
+ else {
+ IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n");
+ }
+
+ /* What is the source video being treated as... */
+ if (itv->yuv_info.frame_interlaced) {
+ IVTV_DEBUG_WARN("Source video: Interlaced\n");
+ }
+ else {
+ IVTV_DEBUG_WARN("Source video: Non-interlaced\n");
+ }
+
+ /* We offset into the image using two different index methods, so split
+ the y source coord into two parts. */
+ if (window->src_y < 8) {
+ src_y_minor_uv = window->src_y;
+ src_y_major_uv = 0;
+ }
+ else {
+ src_y_minor_uv = 8;
+ src_y_major_uv = window->src_y - 8;
+ }
+
+ src_y_minor_y = src_y_minor_uv;
+ src_y_major_y = src_y_major_uv;
+
+ if (window->offset_y) src_y_minor_y += 16;
+
+ if (window->interlaced_y)
+ reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y);
+ else
+ reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1);
+
+ if (window->interlaced_uv)
+ reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1);
+ else
+ reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv);
+
+ reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14;
+ reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14;
+
+ if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) {
+ master_height = (window->src_h * 0x00400000) / window->dst_h;
+ if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++;
+ reg_2920 = master_height >> 2;
+ reg_2928 = master_height >> 3;
+ reg_2930 = master_height;
+ reg_2940 = master_height >> 1;
+ reg_2964_base >>= 3;
+ reg_2968_base >>= 3;
+ reg_296c = 0x00000000;
+ }
+ else if (window->dst_h >= window->src_h) {
+ master_height = (window->src_h * 0x00400000) / window->dst_h;
+ master_height = (master_height >> 1) + (master_height & 1);
+ reg_2920 = master_height >> 2;
+ reg_2928 = master_height >> 2;
+ reg_2930 = master_height;
+ reg_2940 = master_height >> 1;
+ reg_296c = 0x00000000;
+ if (window->interlaced_y) {
+ reg_2964_base >>= 3;
+ }
+ else {
+ reg_296c ++;
+ reg_2964_base >>= 2;
+ }
+ if (window->interlaced_uv) reg_2928 >>= 1;
+ reg_2968_base >>= 3;
+ }
+ else if (window->dst_h >= window->src_h / 2) {
+ master_height = (window->src_h * 0x00200000) / window->dst_h;
+ master_height = (master_height >> 1) + (master_height & 1);
+ reg_2920 = master_height >> 2;
+ reg_2928 = master_height >> 2;
+ reg_2930 = master_height;
+ reg_2940 = master_height;
+ reg_296c = 0x00000101;
+ if (window->interlaced_y) {
+ reg_2964_base >>= 2;
+ }
+ else {
+ reg_296c ++;
+ reg_2964_base >>= 1;
+ }
+ if (window->interlaced_uv) reg_2928 >>= 1;
+ reg_2968_base >>= 2;
+ }
+ else {
+ master_height = (window->src_h * 0x00100000) / window->dst_h;
+ master_height = (master_height >> 1) + (master_height & 1);
+ reg_2920 = master_height >> 2;
+ reg_2928 = master_height >> 2;
+ reg_2930 = master_height;
+ reg_2940 = master_height;
+ reg_2964_base >>= 1;
+ reg_2968_base >>= 2;
+ reg_296c = 0x00000102;
+ }
+
+ /* FIXME These registers change depending on scaled / unscaled output
+ We really need to work out what they should be */
+ if (window->src_h == window->dst_h){
+ reg_2934 = 0x00020000;
+ reg_293c = 0x00100000;
+ reg_2944 = 0x00040000;
+ reg_294c = 0x000b0000;
+ }
+ else {
+ reg_2934 = 0x00000FF0;
+ reg_293c = 0x00000FF0;
+ reg_2944 = 0x00000FF0;
+ reg_294c = 0x00000FF0;
+ }
+
+ /* The first line to be displayed */
+ reg_2950 = 0x00010000 + src_y_major_y;
+ if (window->interlaced_y) reg_2950 += 0x00010000;
+ reg_2954 = reg_2950 + 1;
+
+ reg_2958 = 0x00010000 + (src_y_major_y >> 1);
+ if (window->interlaced_uv) reg_2958 += 0x00010000;
+ reg_295c = reg_2958 + 1;
+
+ if (itv->yuv_info.decode_height == 480)
+ reg_289c = 0x011e0017;
+ else
+ reg_289c = 0x01500017;
+
+ if (window->dst_y < 0)
+ reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1);
+ else
+ reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1);
+
+ /* How much of the source to decode.
+ Take into account the source offset */
+ reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) |
+ ((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15);
+
+ /* Calculate correct value for register 2964 */
+ if (window->src_h == window->dst_h)
+ reg_2964 = 1;
+ else {
+ reg_2964 = 2 + ((window->dst_h << 1) / window->src_h);
+ reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1);
+ }
+ reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1);
+ reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94);
+
+ /* Okay, we've wasted time working out the correct value,
+ but if we use it, it fouls the the window alignment.
+ Fudge it to what we want... */
+ reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16));
+ reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16));
+
+ /* Deviate further from what it should be. I find the flicker headache
+ inducing so try to reduce it slightly. Leave 2968 as-is otherwise
+ colours foul. */
+ if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h))
+ reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2);
+
+ if (!window->interlaced_y) reg_2964 -= 0x00010001;
+ if (!window->interlaced_uv) reg_2968 -= 0x00010001;
+
+ reg_2964 += ((reg_2964_base << 16) | reg_2964_base);
+ reg_2968 += ((reg_2968_base << 16) | reg_2968_base);
+
+ /* Select the vertical filter */
+ if (window->src_h == window->dst_h) {
+ /* An exact size match uses filter 0/1 */
+ v_filter_1 = 0;
+ v_filter_2 = 1;
+ }
+ else {
+ /* Figure out which filter to use */
+ v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15;
+ v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
+ /* Only an exact size match can use filter 0 */
+ if (v_filter_1 == 0) v_filter_1 = 1;
+ v_filter_2 = v_filter_1;
+ }
+
+ write_reg(reg_2934, 0x02934);
+ write_reg(reg_293c, 0x0293c);
+ IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c);
+ write_reg(reg_2944, 0x02944);
+ write_reg(reg_294c, 0x0294c);
+ IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c);
+
+ /* Ensure 2970 is 0 (does it ever change ?) */
+/* write_reg(0,0x02970); */
+/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */
+
+ write_reg(reg_2930, 0x02938);
+ write_reg(reg_2930, 0x02930);
+ IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930);
+
+ write_reg(reg_2928, 0x02928);
+ write_reg(reg_2928+0x514, 0x0292C);
+ IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514);
+
+ write_reg(reg_2920, 0x02920);
+ write_reg(reg_2920+0x514, 0x02924);
+ IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920);
+
+ write_reg (reg_2918,0x02918);
+ write_reg (reg_291c,0x0291C);
+ IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c);
+
+ write_reg(reg_296c, 0x0296c);
+ IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c);
+
+ write_reg(reg_2940, 0x02948);
+ write_reg(reg_2940, 0x02940);
+ IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940);
+
+ write_reg(reg_2950, 0x02950);
+ write_reg(reg_2954, 0x02954);
+ IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954);
+
+ write_reg(reg_2958, 0x02958);
+ write_reg(reg_295c, 0x0295C);
+ IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c);
+
+ write_reg(reg_2960, 0x02960);
+ IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960);
+
+ write_reg(reg_2964, 0x02964);
+ write_reg(reg_2968, 0x02968);
+ IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968);
+
+ write_reg( reg_289c,0x0289c);
+ IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c);
+
+ /* Only update filter 1 if we really need to */
+ if (v_filter_1 != itv->yuv_info.v_filter_1) {
+ ivtv_yuv_filter (itv,-1,v_filter_1,-1);
+ itv->yuv_info.v_filter_1 = v_filter_1;
+ }
+
+ /* Only update filter 2 if we really need to */
+ if (v_filter_2 != itv->yuv_info.v_filter_2) {
+ ivtv_yuv_filter (itv,-1,-1,v_filter_2);
+ itv->yuv_info.v_filter_2 = v_filter_2;
+ }
+
+ itv->yuv_info.frame_interlaced_last = itv->yuv_info.frame_interlaced;
+}
+
+/* Modify the supplied coordinate information to fit the visible osd area */
+static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window)
+{
+ int osd_crop, lace_threshold;
+ u32 osd_scale;
+ u32 yuv_update = 0;
+
+ lace_threshold = itv->yuv_info.lace_threshold;
+ if (lace_threshold < 0)
+ lace_threshold = itv->yuv_info.decode_height - 1;
+
+ /* Work out the lace settings */
+ switch (itv->yuv_info.lace_mode) {
+ case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
+ itv->yuv_info.frame_interlaced = 0;
+ if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021))
+ window->interlaced_y = 0;
+ else
+ window->interlaced_y = 1;
+
+ if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
+ window->interlaced_uv = 0;
+ else
+ window->interlaced_uv = 1;
+ break;
+
+ case IVTV_YUV_MODE_AUTO:
+ if (window->tru_h <= lace_threshold || window->tru_h > 576 || window->tru_w > 720){
+ itv->yuv_info.frame_interlaced = 0;
+ if ((window->tru_h < 512) ||
+ (window->tru_h > 576 && window->tru_h < 1021) ||
+ (window->tru_w > 720 && window->tru_h < 1021))
+ window->interlaced_y = 0;
+ else
+ window->interlaced_y = 1;
+
+ if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
+ window->interlaced_uv = 0;
+ else
+ window->interlaced_uv = 1;
+ }
+ else {
+ itv->yuv_info.frame_interlaced = 1;
+ window->interlaced_y = 1;
+ window->interlaced_uv = 1;
+ }
+ break;
+
+ case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
+ default:
+ itv->yuv_info.frame_interlaced = 1;
+ window->interlaced_y = 1;
+ window->interlaced_uv = 1;
+ break;
+ }
+
+ /* Sorry, but no negative coords for src */
+ if (window->src_x < 0) window->src_x = 0;
+ if (window->src_y < 0) window->src_y = 0;
+
+ /* Can only reduce width down to 1/4 original size */
+ if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) {
+ window->src_x += osd_crop / 2;
+ window->src_w = (window->src_w - osd_crop) & ~3;
+ window->dst_w = window->src_w / 4;
+ window->dst_w += window->dst_w & 1;
+ }
+
+ /* Can only reduce height down to 1/4 original size */
+ if (window->src_h / window->dst_h >= 2) {
+ /* Overflow may be because we're running progressive, so force mode switch */
+ window->interlaced_y = 1;
+ /* Make sure we're still within limits for interlace */
+ if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) {
+ /* If we reach here we'll have to force the height. */
+ window->src_y += osd_crop / 2;
+ window->src_h = (window->src_h - osd_crop) & ~3;
+ window->dst_h = window->src_h / 4;
+ window->dst_h += window->dst_h & 1;
+ }
+ }
+
+ /* If there's nothing to safe to display, we may as well stop now */
+ if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+ return 0;
+ }
+
+ /* Ensure video remains inside OSD area */
+ osd_scale = (window->src_h << 16) / window->dst_h;
+
+ if ((osd_crop = window->pan_y - window->dst_y) > 0) {
+ /* Falls off the upper edge - crop */
+ window->src_y += (osd_scale * osd_crop) >> 16;
+ window->src_h -= (osd_scale * osd_crop) >> 16;
+ window->dst_h -= osd_crop;
+ window->dst_y = 0;
+ }
+ else {
+ window->dst_y -= window->pan_y;
+ }
+
+ if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) {
+ /* Falls off the lower edge - crop */
+ window->dst_h -= osd_crop;
+ window->src_h -= (osd_scale * osd_crop) >> 16;
+ }
+
+ osd_scale = (window->src_w << 16) / window->dst_w;
+
+ if ((osd_crop = window->pan_x - window->dst_x) > 0) {
+ /* Fall off the left edge - crop */
+ window->src_x += (osd_scale * osd_crop) >> 16;
+ window->src_w -= (osd_scale * osd_crop) >> 16;
+ window->dst_w -= osd_crop;
+ window->dst_x = 0;
+ }
+ else {
+ window->dst_x -= window->pan_x;
+ }
+
+ if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) {
+ /* Falls off the right edge - crop */
+ window->dst_w -= osd_crop;
+ window->src_w -= (osd_scale * osd_crop) >> 16;
+ }
+
+ /* The OSD can be moved. Track to it */
+ window->dst_x += itv->yuv_info.osd_x_offset;
+ window->dst_y += itv->yuv_info.osd_y_offset;
+
+ /* Width & height for both src & dst must be even.
+ Same for coordinates. */
+ window->dst_w &= ~1;
+ window->dst_x &= ~1;
+
+ window->src_w += window->src_x & 1;
+ window->src_x &= ~1;
+
+ window->src_w &= ~1;
+ window->dst_w &= ~1;
+
+ window->dst_h &= ~1;
+ window->dst_y &= ~1;
+
+ window->src_h += window->src_y & 1;
+ window->src_y &= ~1;
+
+ window->src_h &= ~1;
+ window->dst_h &= ~1;
+
+ /* Due to rounding, we may have reduced the output size to <1/4 of the source
+ Check again, but this time just resize. Don't change source coordinates */
+ if (window->dst_w < window->src_w / 4) {
+ window->src_w &= ~3;
+ window->dst_w = window->src_w / 4;
+ window->dst_w += window->dst_w & 1;
+ }
+ if (window->dst_h < window->src_h / 4) {
+ window->src_h &= ~3;
+ window->dst_h = window->src_h / 4;
+ window->dst_h += window->dst_h & 1;
+ }
+
+ /* Check again. If there's nothing to safe to display, stop now */
+ if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+ return 0;
+ }
+
+ /* Both x offset & width are linked, so they have to be done together */
+ if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) ||
+ (itv->yuv_info.old_frame_info.src_w != window->src_w) ||
+ (itv->yuv_info.old_frame_info.dst_x != window->dst_x) ||
+ (itv->yuv_info.old_frame_info.src_x != window->src_x) ||
+ (itv->yuv_info.old_frame_info.pan_x != window->pan_x) ||
+ (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) {
+ yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL;
+ }
+
+ if ((itv->yuv_info.old_frame_info.src_h != window->src_h) ||
+ (itv->yuv_info.old_frame_info.dst_h != window->dst_h) ||
+ (itv->yuv_info.old_frame_info.dst_y != window->dst_y) ||
+ (itv->yuv_info.old_frame_info.src_y != window->src_y) ||
+ (itv->yuv_info.old_frame_info.pan_y != window->pan_y) ||
+ (itv->yuv_info.old_frame_info.vis_h != window->vis_h) ||
+ (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) ||
+ (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) {
+ yuv_update |= IVTV_YUV_UPDATE_VERTICAL;
+ }
+
+ return yuv_update;
+}
+
+/* Update the scaling register to the requested value */
+void ivtv_yuv_work_handler (struct ivtv *itv)
+{
+ struct yuv_frame_info window;
+ u32 yuv_update;
+
+ int frame = itv->yuv_info.update_frame;
+
+/* IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */
+ memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window));
+
+ /* Update the osd pan info */
+ window.pan_x = itv->yuv_info.osd_x_pan;
+ window.pan_y = itv->yuv_info.osd_y_pan;
+ window.vis_w = itv->yuv_info.osd_vis_w;
+ window.vis_h = itv->yuv_info.osd_vis_h;
+
+ /* Calculate the display window coordinates. Exit if nothing left */
+ if (!(yuv_update = ivtv_yuv_window_setup (itv, &window)))
+ return;
+
+ /* Update horizontal settings */
+ if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL)
+ ivtv_yuv_handle_horizontal(itv, &window);
+
+ if (yuv_update & IVTV_YUV_UPDATE_VERTICAL)
+ ivtv_yuv_handle_vertical(itv, &window);
+
+ memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info));
+}
+
+static void ivtv_yuv_init (struct ivtv *itv)
+{
+ IVTV_DEBUG_YUV("ivtv_yuv_init\n");
+
+ /* Take a snapshot of the current register settings */
+ itv->yuv_info.reg_2834 = read_reg(0x02834);
+ itv->yuv_info.reg_2838 = read_reg(0x02838);
+ itv->yuv_info.reg_283c = read_reg(0x0283c);
+ itv->yuv_info.reg_2840 = read_reg(0x02840);
+ itv->yuv_info.reg_2844 = read_reg(0x02844);
+ itv->yuv_info.reg_2848 = read_reg(0x02848);
+ itv->yuv_info.reg_2854 = read_reg(0x02854);
+ itv->yuv_info.reg_285c = read_reg(0x0285c);
+ itv->yuv_info.reg_2864 = read_reg(0x02864);
+ itv->yuv_info.reg_2870 = read_reg(0x02870);
+ itv->yuv_info.reg_2874 = read_reg(0x02874);
+ itv->yuv_info.reg_2898 = read_reg(0x02898);
+ itv->yuv_info.reg_2890 = read_reg(0x02890);
+
+ itv->yuv_info.reg_289c = read_reg(0x0289c);
+ itv->yuv_info.reg_2918 = read_reg(0x02918);
+ itv->yuv_info.reg_291c = read_reg(0x0291c);
+ itv->yuv_info.reg_2920 = read_reg(0x02920);
+ itv->yuv_info.reg_2924 = read_reg(0x02924);
+ itv->yuv_info.reg_2928 = read_reg(0x02928);
+ itv->yuv_info.reg_292c = read_reg(0x0292c);
+ itv->yuv_info.reg_2930 = read_reg(0x02930);
+ itv->yuv_info.reg_2934 = read_reg(0x02934);
+ itv->yuv_info.reg_2938 = read_reg(0x02938);
+ itv->yuv_info.reg_293c = read_reg(0x0293c);
+ itv->yuv_info.reg_2940 = read_reg(0x02940);
+ itv->yuv_info.reg_2944 = read_reg(0x02944);
+ itv->yuv_info.reg_2948 = read_reg(0x02948);
+ itv->yuv_info.reg_294c = read_reg(0x0294c);
+ itv->yuv_info.reg_2950 = read_reg(0x02950);
+ itv->yuv_info.reg_2954 = read_reg(0x02954);
+ itv->yuv_info.reg_2958 = read_reg(0x02958);
+ itv->yuv_info.reg_295c = read_reg(0x0295c);
+ itv->yuv_info.reg_2960 = read_reg(0x02960);
+ itv->yuv_info.reg_2964 = read_reg(0x02964);
+ itv->yuv_info.reg_2968 = read_reg(0x02968);
+ itv->yuv_info.reg_296c = read_reg(0x0296c);
+ itv->yuv_info.reg_2970 = read_reg(0x02970);
+
+ itv->yuv_info.v_filter_1 = -1;
+ itv->yuv_info.v_filter_2 = -1;
+ itv->yuv_info.h_filter = -1;
+
+ /* Set some valid size info */
+ itv->yuv_info.osd_x_offset = read_reg(0x02a04) & 0x00000FFF;
+ itv->yuv_info.osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF;
+
+ /* Bit 2 of reg 2878 indicates current decoder output format
+ 0 : NTSC 1 : PAL */
+ if (read_reg(0x2878) & 4)
+ itv->yuv_info.decode_height = 576;
+ else
+ itv->yuv_info.decode_height = 480;
+
+ /* If no visible size set, assume full size */
+ if (!itv->yuv_info.osd_vis_w) itv->yuv_info.osd_vis_w = 720 - itv->yuv_info.osd_x_offset;
+ if (!itv->yuv_info.osd_vis_h) itv->yuv_info.osd_vis_h = itv->yuv_info.decode_height - itv->yuv_info.osd_y_offset;
+
+ /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
+ itv->yuv_info.blanking_ptr = kzalloc(720*16,GFP_KERNEL);
+ if (itv->yuv_info.blanking_ptr) {
+ itv->yuv_info.blanking_dmaptr = pci_map_single(itv->dev, itv->yuv_info.blanking_ptr, 720*16, PCI_DMA_TODEVICE);
+ }
+ else {
+ itv->yuv_info.blanking_dmaptr = 0;
+ IVTV_DEBUG_WARN ("Failed to allocate yuv blanking buffer\n");
+ }
+
+ IVTV_DEBUG_WARN("Enable video output\n");
+ write_reg_sync(0x00108080, 0x2898);
+
+ /* Enable YUV decoder output */
+ write_reg_sync(0x01, IVTV_REG_VDM);
+
+ set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+ atomic_set(&itv->yuv_info.next_dma_frame,0);
+}
+
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+ DEFINE_WAIT(wait);
+ int rc = 0;
+ int got_sig = 0;
+ int frame, next_fill_frame, last_fill_frame;
+
+ IVTV_DEBUG_INFO("yuv_prep_frame\n");
+
+ if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv);
+
+ frame = atomic_read(&itv->yuv_info.next_fill_frame);
+ next_fill_frame = (frame + 1) & 0x3;
+ last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3;
+
+ if (next_fill_frame != last_fill_frame && last_fill_frame != frame) {
+ /* Buffers are full - Overwrite the last frame */
+ next_fill_frame = frame;
+ frame = (frame - 1) & 3;
+ }
+
+ /* Take a snapshot of the yuv coordinate information */
+ itv->yuv_info.new_frame_info[frame].src_x = args->src.left;
+ itv->yuv_info.new_frame_info[frame].src_y = args->src.top;
+ itv->yuv_info.new_frame_info[frame].src_w = args->src.width;
+ itv->yuv_info.new_frame_info[frame].src_h = args->src.height;
+ itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left;
+ itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top;
+ itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width;
+ itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height;
+ itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left;
+ itv->yuv_info.new_frame_info[frame].tru_w = args->src_width;
+ itv->yuv_info.new_frame_info[frame].tru_h = args->src_height;
+
+ /* Are we going to offset the Y plane */
+ if (args->src.height + args->src.top < 512-16)
+ itv->yuv_info.new_frame_info[frame].offset_y = 1;
+ else
+ itv->yuv_info.new_frame_info[frame].offset_y = 0;
+
+ /* Snapshot the osd pan info */
+ itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan;
+ itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan;
+ itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w;
+ itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h;
+
+ itv->yuv_info.new_frame_info[frame].update = 0;
+ itv->yuv_info.new_frame_info[frame].interlaced_y = 0;
+ itv->yuv_info.new_frame_info[frame].interlaced_uv = 0;
+
+ if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame],
+ sizeof (itv->yuv_info.new_frame_info[frame]))) {
+ memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args));
+ itv->yuv_info.new_frame_info[frame].update = 1;
+/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */
+ }
+
+ /* DMA the frame */
+ mutex_lock(&itv->udma.lock);
+
+ if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) {
+ mutex_unlock(&itv->udma.lock);
+ return rc;
+ }
+
+ ivtv_udma_prepare(itv);
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ /* if no UDMA is pending and no UDMA is in progress, then the DMA
+ is finished */
+ while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
+ /* don't interrupt if the DMA is in progress but break off
+ a still pending DMA. */
+ got_sig = signal_pending(current);
+ if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
+ break;
+ got_sig = 0;
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+
+ /* Unmap Last DMA Xfer */
+ ivtv_udma_unmap(itv);
+
+ if (got_sig) {
+ IVTV_DEBUG_INFO("User stopped YUV UDMA\n");
+ mutex_unlock(&itv->udma.lock);
+ return -EINTR;
+ }
+
+ atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame);
+
+ mutex_unlock(&itv->udma.lock);
+ return rc;
+}
+
+void ivtv_yuv_close(struct ivtv *itv)
+{
+ int h_filter, v_filter_1, v_filter_2;
+
+ IVTV_DEBUG_YUV("ivtv_yuv_close\n");
+ ivtv_waitq(&itv->vsync_waitq);
+
+ atomic_set(&itv->yuv_info.next_dma_frame, -1);
+ atomic_set(&itv->yuv_info.next_fill_frame, 0);
+
+ /* Reset registers we have changed so mpeg playback works */
+
+ /* If we fully restore this register, the display may remain active.
+ Restore, but set one bit to blank the video. Firmware will always
+ clear this bit when needed, so not a problem. */
+ write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898);
+
+ write_reg(itv->yuv_info.reg_2834, 0x02834);
+ write_reg(itv->yuv_info.reg_2838, 0x02838);
+ write_reg(itv->yuv_info.reg_283c, 0x0283c);
+ write_reg(itv->yuv_info.reg_2840, 0x02840);
+ write_reg(itv->yuv_info.reg_2844, 0x02844);
+ write_reg(itv->yuv_info.reg_2848, 0x02848);
+ write_reg(itv->yuv_info.reg_2854, 0x02854);
+ write_reg(itv->yuv_info.reg_285c, 0x0285c);
+ write_reg(itv->yuv_info.reg_2864, 0x02864);
+ write_reg(itv->yuv_info.reg_2870, 0x02870);
+ write_reg(itv->yuv_info.reg_2874, 0x02874);
+ write_reg(itv->yuv_info.reg_2890, 0x02890);
+ write_reg(itv->yuv_info.reg_289c, 0x0289c);
+
+ write_reg(itv->yuv_info.reg_2918, 0x02918);
+ write_reg(itv->yuv_info.reg_291c, 0x0291c);
+ write_reg(itv->yuv_info.reg_2920, 0x02920);
+ write_reg(itv->yuv_info.reg_2924, 0x02924);
+ write_reg(itv->yuv_info.reg_2928, 0x02928);
+ write_reg(itv->yuv_info.reg_292c, 0x0292c);
+ write_reg(itv->yuv_info.reg_2930, 0x02930);
+ write_reg(itv->yuv_info.reg_2934, 0x02934);
+ write_reg(itv->yuv_info.reg_2938, 0x02938);
+ write_reg(itv->yuv_info.reg_293c, 0x0293c);
+ write_reg(itv->yuv_info.reg_2940, 0x02940);
+ write_reg(itv->yuv_info.reg_2944, 0x02944);
+ write_reg(itv->yuv_info.reg_2948, 0x02948);
+ write_reg(itv->yuv_info.reg_294c, 0x0294c);
+ write_reg(itv->yuv_info.reg_2950, 0x02950);
+ write_reg(itv->yuv_info.reg_2954, 0x02954);
+ write_reg(itv->yuv_info.reg_2958, 0x02958);
+ write_reg(itv->yuv_info.reg_295c, 0x0295c);
+ write_reg(itv->yuv_info.reg_2960, 0x02960);
+ write_reg(itv->yuv_info.reg_2964, 0x02964);
+ write_reg(itv->yuv_info.reg_2968, 0x02968);
+ write_reg(itv->yuv_info.reg_296c, 0x0296c);
+ write_reg(itv->yuv_info.reg_2970, 0x02970);
+
+ /* Prepare to restore filters */
+
+ /* First the horizontal filter */
+ if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) {
+ /* An exact size match uses filter 0 */
+ h_filter = 0;
+ }
+ else {
+ /* Figure out which filter to use */
+ h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15;
+ h_filter = (h_filter >> 1) + (h_filter & 1);
+ /* Only an exact size match can use filter 0. */
+ if (h_filter < 1) h_filter = 1;
+ }
+
+ /* Now the vertical filter */
+ if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) {
+ /* An exact size match uses filter 0/1 */
+ v_filter_1 = 0;
+ v_filter_2 = 1;
+ }
+ else {
+ /* Figure out which filter to use */
+ v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15;
+ v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
+ /* Only an exact size match can use filter 0 */
+ if (v_filter_1 == 0) v_filter_1 = 1;
+ v_filter_2 = v_filter_1;
+ }
+
+ /* Now restore the filters */
+ ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2);
+
+ /* and clear a few registers */
+ write_reg(0, 0x02814);
+ write_reg(0, 0x0282c);
+ write_reg(0, 0x02904);
+ write_reg(0, 0x02910);
+
+ /* Release the blanking buffer */
+ if (itv->yuv_info.blanking_ptr) {
+ kfree (itv->yuv_info.blanking_ptr);
+ itv->yuv_info.blanking_ptr = NULL;
+ pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
+ }
+
+ /* Invalidate the old dimension information */
+ itv->yuv_info.old_frame_info.src_w = 0;
+ itv->yuv_info.old_frame_info.src_h = 0;
+ itv->yuv_info.old_frame_info_args.src_w = 0;
+ itv->yuv_info.old_frame_info_args.src_h = 0;
+
+ /* All done. */
+ clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+}
+
diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h
new file mode 100644
index 000000000000..88972d3f77c4
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-yuv.h
@@ -0,0 +1,24 @@
+/*
+ yuv support
+
+ Copyright (C) 2007 Ian Armstrong <ian@iarmst.demon.co.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 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
+ */
+
+int ivtv_yuv_filter_check(struct ivtv *itv);
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args);
+void ivtv_yuv_close(struct ivtv *itv);
+void ivtv_yuv_work_handler (struct ivtv *itv);
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index ba1af3c8525e..3bb7d6634862 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -773,6 +773,9 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
break;
}
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, state->ident, (state->rev1 << 16) | state->rev2);
+
default:
/* unknown */
return -EINVAL;
@@ -872,6 +875,8 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d",
msp_family, msp_product,
msp_revision, msp_hard, msp_rom);
+ /* Rev B=2, C=3, D=4, G=7 */
+ state->ident = msp_family * 10000 + 4000 + msp_product * 10 + msp_revision - '@';
/* Has NICAM support: all mspx41x and mspx45x products have NICAM */
state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5;
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
index 7531efa1615e..ab69a290e5dc 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/video/msp3400-driver.h
@@ -50,6 +50,7 @@ extern int msp_stereo_thresh;
struct msp_state {
int rev1, rev2;
+ int ident;
u8 has_nicam;
u8 has_radio;
u8 has_headphones;
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index 5ed0adc4ca26..03bc369a9e49 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -5,6 +5,8 @@
* by Jonathan Corbet with substantial inspiration from Mark
* McClelland's ovcamchip code.
*
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
* This file may be distributed under the terms of the GNU General
* Public License, version 2.
*/
@@ -15,6 +17,7 @@
#include <linux/delay.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#include <linux/i2c.h>
@@ -162,6 +165,10 @@ MODULE_LICENSE("GPL");
#define REG_GFIX 0x69 /* Fix gain control */
+#define REG_REG76 0x76 /* OV's name */
+#define R76_BLKPCOR 0x80 /* Black pixel correction enable */
+#define R76_WHTPCOR 0x40 /* White pixel correction enable */
+
#define REG_RGB444 0x8c /* RGB 444 control */
#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */
#define R444_RGBX 0x01 /* Empty nibble at end */
@@ -255,7 +262,7 @@ static struct regval_list ov7670_default_regs[] = {
/* Almost all of these are magic "reserved" values. */
{ REG_COM5, 0x61 }, { REG_COM6, 0x4b },
- { 0x16, 0x02 }, { REG_MVFP, 0x07|MVFP_MIRROR },
+ { 0x16, 0x02 }, { REG_MVFP, 0x07 },
{ 0x21, 0x02 }, { 0x22, 0x91 },
{ 0x29, 0x07 }, { 0x33, 0x0b },
{ 0x35, 0x0b }, { 0x37, 0x1d },
@@ -380,6 +387,13 @@ static struct regval_list ov7670_fmt_rgb444[] = {
{ 0xff, 0xff },
};
+static struct regval_list ov7670_fmt_raw[] = {
+ { REG_COM7, COM7_BAYER },
+ { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */
+ { REG_COM16, 0x3d }, /* Edge enhancement, denoise */
+ { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */
+ { 0xff, 0xff },
+};
@@ -483,32 +497,39 @@ static struct ov7670_format_struct {
__u32 pixelformat;
struct regval_list *regs;
int cmatrix[CMATRIX_LEN];
+ int bpp; /* Bytes per pixel */
} ov7670_formats[] = {
{
.desc = "YUYV 4:2:2",
.pixelformat = V4L2_PIX_FMT_YUYV,
.regs = ov7670_fmt_yuv422,
.cmatrix = { 128, -128, 0, -34, -94, 128 },
+ .bpp = 2,
},
{
.desc = "RGB 444",
.pixelformat = V4L2_PIX_FMT_RGB444,
.regs = ov7670_fmt_rgb444,
.cmatrix = { 179, -179, 0, -61, -176, 228 },
+ .bpp = 2,
},
{
.desc = "RGB 565",
.pixelformat = V4L2_PIX_FMT_RGB565,
.regs = ov7670_fmt_rgb565,
.cmatrix = { 179, -179, 0, -61, -176, 228 },
+ .bpp = 2,
+ },
+ {
+ .desc = "Raw RGB Bayer",
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .regs = ov7670_fmt_raw,
+ .cmatrix = { 0, 0, 0, 0, 0, 0 },
+ .bpp = 1
},
};
-#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
+#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
-/*
- * All formats we support are 2 bytes/pixel.
- */
-#define BYTES_PER_PIXEL 2
/*
* Then there is the issue of window sizes. Try to capture the info here.
@@ -685,7 +706,7 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
*/
pix->width = wsize->width;
pix->height = wsize->height;
- pix->bytesperline = pix->width*BYTES_PER_PIXEL;
+ pix->bytesperline = pix->width*ov7670_formats[index].bpp;
pix->sizeimage = pix->height*pix->bytesperline;
return 0;
}
@@ -1270,9 +1291,8 @@ static int ov7670_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
switch (cmd) {
- case VIDIOC_INT_G_CHIP_IDENT:
- * (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670;
- return 0;
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0);
case VIDIOC_INT_RESET:
ov7670_reset(client);
diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c
index e6e61df0d071..1455a8f4e930 100644
--- a/drivers/media/video/planb.c
+++ b/drivers/media/video/planb.c
@@ -2210,7 +2210,7 @@ static int find_planb(void)
"membase 0x%x (base reg. 0x%x)\n",
bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg);
- pdev = pci_find_slot (bus, dev_fn);
+ pdev = pci_get_bus_and_slot(bus, dev_fn);
if (!pdev) {
printk(KERN_ERR "planb: cannot find slot\n");
goto err_out;
@@ -2240,6 +2240,7 @@ static int find_planb(void)
pb->planb_base = planb_regs;
pb->planb_base_phys = (struct planb_registers *)new_base;
pb->irq = irq;
+ pb->dev = pdev;
return planb_num;
@@ -2247,6 +2248,7 @@ err_out_disable:
pci_disable_device(pdev);
err_out:
/* FIXME handle error */ /* comment moved from pci_find_slot, above */
+ pci_dev_put(pdev);
return 0;
}
@@ -2274,6 +2276,8 @@ static void release_planb(void)
printk(KERN_INFO "PlanB: unregistering with v4l\n");
video_unregister_device(&pb->video_dev);
+ pci_dev_put(pb->dev);
+
/* note that iounmap() does nothing on the PPC right now */
iounmap ((void *)pb->planb_base);
}
diff --git a/drivers/media/video/planb.h b/drivers/media/video/planb.h
index 92823211d0c5..e21b5735c103 100644
--- a/drivers/media/video/planb.h
+++ b/drivers/media/video/planb.h
@@ -177,6 +177,7 @@ struct planb {
struct mutex lock;
unsigned int irq; /* interrupt number */
volatile unsigned int intr_mask;
+ struct pci_dev *dev; /* Our PCI device */
int overlay; /* overlay running? */
struct planb_window win;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index 5786faf9b3b8..5669c8ca9ca3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -324,7 +324,7 @@ static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
/* This implements some extra setup for the encoder that seems to be
specific to the PVR USB2 hardware. */
-int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
+static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
{
int ret = 0;
int encMisc3Arg = 0;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index 16bd74199601..ce66ab8ff2d8 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -283,6 +283,8 @@ struct pvr2_hdw {
int unit_number; /* ID for driver instance */
unsigned long serial_number; /* ID for hardware itself */
+ char bus_info[32]; /* Bus location info */
+
/* Minor numbers used by v4l logic (yes, this is a hack, as there
should be no v4l junk here). Probably a better way to do this. */
int v4l_minor_number_video;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 9916cf32494d..acf651e01f94 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -1008,6 +1008,13 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
return hdw->serial_number;
}
+
+const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
+{
+ return hdw->bus_info;
+}
+
+
unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
{
return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
@@ -2105,6 +2112,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->usb_intf = intf;
hdw->usb_dev = interface_to_usbdev(intf);
+ scnprintf(hdw->bus_info,sizeof(hdw->bus_info),
+ "usb %s address %d",
+ hdw->usb_dev->dev.bus_id,
+ hdw->usb_dev->devnum);
+
ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
usb_set_interface(hdw->usb_dev,ifnum,0);
@@ -3275,7 +3287,9 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw,
mutex_lock(&hdw->i2c_list_lock); do {
list_for_each(item,&hdw->i2c_clients) {
cp = list_entry(item,struct pvr2_i2c_client,list);
- if (!v4l2_chip_match_i2c_client(cp->client, req.match_type, req.match_chip)) {
+ if (!v4l2_chip_match_i2c_client(
+ cp->client,
+ req.match_type, req.match_chip)) {
continue;
}
stat = pvr2_i2c_client_cmd(
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 0c9cca43ff85..4dba8d006324 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -124,6 +124,9 @@ struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *);
/* Retrieve serial number of device */
unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *);
+/* Retrieve bus location info of device */
+const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *);
+
/* Called when hardware has been unplugged */
void pvr2_hdw_disconnect(struct pvr2_hdw *);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 91396fd573e4..a741c556a39a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -42,9 +42,11 @@ struct pvr2_sysfs {
struct class_device_attribute attr_v4l_minor_number;
struct class_device_attribute attr_v4l_radio_minor_number;
struct class_device_attribute attr_unit_number;
+ struct class_device_attribute attr_bus_info;
int v4l_minor_number_created_ok;
int v4l_radio_minor_number_created_ok;
int unit_number_created_ok;
+ int bus_info_created_ok;
};
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
@@ -705,6 +707,10 @@ static void class_dev_destroy(struct pvr2_sysfs *sfp)
pvr2_sysfs_tear_down_debugifc(sfp);
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
pvr2_sysfs_tear_down_controls(sfp);
+ if (sfp->bus_info_created_ok) {
+ class_device_remove_file(sfp->class_dev,
+ &sfp->attr_bus_info);
+ }
if (sfp->v4l_minor_number_created_ok) {
class_device_remove_file(sfp->class_dev,
&sfp->attr_v4l_minor_number);
@@ -735,6 +741,16 @@ static ssize_t v4l_minor_number_show(struct class_device *class_dev,char *buf)
}
+static ssize_t bus_info_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%s\n",
+ pvr2_hdw_get_bus_info(sfp->channel.hdw));
+}
+
+
static ssize_t v4l_radio_minor_number_show(struct class_device *class_dev,
char *buf)
{
@@ -836,6 +852,20 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
sfp->unit_number_created_ok = !0;
}
+ sfp->attr_bus_info.attr.owner = THIS_MODULE;
+ sfp->attr_bus_info.attr.name = "bus_info_str";
+ sfp->attr_bus_info.attr.mode = S_IRUGO;
+ sfp->attr_bus_info.show = bus_info_show;
+ sfp->attr_bus_info.store = NULL;
+ ret = class_device_create_file(sfp->class_dev,
+ &sfp->attr_bus_info);
+ if (ret < 0) {
+ printk(KERN_WARNING "%s: class_device_create_file error: %d\n",
+ __FUNCTION__, ret);
+ } else {
+ sfp->bus_info_created_ok = !0;
+ }
+
pvr2_sysfs_add_controls(sfp);
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
pvr2_sysfs_add_debugifc(sfp);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 25d3830b482a..4563b3df8a0d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -203,6 +203,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
struct v4l2_capability *cap = arg;
memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+ strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),
+ sizeof(cap->bus_info));
ret = 0;
break;
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
index 0bd115588f31..338ced7188f2 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -140,6 +140,8 @@ static const char *size2name[PSZ_MAX] =
An alternate value of 0 means this mode is not available at all.
*/
+#define PWC_FPS_MAX_NALA 8
+
struct Nala_table_entry {
char alternate; /* USB alternate setting */
int compressed; /* Compressed yes/no */
@@ -147,7 +149,9 @@ struct Nala_table_entry {
unsigned char mode[3]; /* precomputed mode table */
};
-static struct Nala_table_entry Nala_table[PSZ_MAX][8] =
+static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 };
+
+static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] =
{
#include "pwc-nala.h"
};
@@ -423,6 +427,59 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame
return 0;
}
+static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < PWC_FPS_MAX_NALA; i++) {
+ if (Nala_table[size][i].alternate) {
+ if (index--==0) return Nala_fps_vector[i];
+ }
+ }
+ return 0;
+}
+
+static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < PWC_FPS_MAX_KIARA; i++) {
+ if (Kiara_table[size][i][3].alternate) {
+ if (index--==0) return Kiara_fps_vector[i];
+ }
+ }
+ return 0;
+}
+
+static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+ unsigned int i;
+
+ for (i=0; i < PWC_FPS_MAX_TIMON; i++) {
+ if (Timon_table[size][i][3].alternate) {
+ if (index--==0) return Timon_fps_vector[i];
+ }
+ }
+ return 0;
+}
+
+unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+ unsigned int ret;
+
+ if (DEVICE_USE_CODEC1(pdev->type)) {
+ ret = pwc_get_fps_Nala(pdev, index, size);
+
+ } else if (DEVICE_USE_CODEC3(pdev->type)) {
+ ret = pwc_get_fps_Kiara(pdev, index, size);
+
+ } else {
+ ret = pwc_get_fps_Timon(pdev, index, size);
+ }
+
+ return ret;
+}
+
#define BLACK_Y 0
#define BLACK_U 128
#define BLACK_V 128
@@ -1343,7 +1400,7 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red);
if (ret < 0)
break;
- ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
+ ret = pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
if (ret < 0)
break;
}
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 27ed76986ca2..085332a503de 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -95,8 +95,8 @@ static const struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
{ USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
{ USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
- { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */
- { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */
+ { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */
+ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */
{ USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
{ USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
{ USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
@@ -1493,7 +1493,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
case 0x0329:
PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
name = "Philips SPC 900NC webcam";
- type_id = 720;
+ type_id = 740;
break;
default:
return -ENODEV;
@@ -1547,8 +1547,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
features |= FEATURE_MOTOR_PANTILT;
break;
case 0x08b6:
+ PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
+ name = "Cisco VT Camera";
+ type_id = 740; /* CCD sensor */
+ break;
case 0x08b7:
- case 0x08b8:
+ PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
+ name = "Logitech ViewPort AV 100";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b8: /* Where this released? */
PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
name = "Logitech QuickCam (res.)";
type_id = 730; /* Assuming CMOS */
diff --git a/drivers/media/video/pwc/pwc-ioctl.h b/drivers/media/video/pwc/pwc-ioctl.h
index 784bc72521fa..cec660299768 100644
--- a/drivers/media/video/pwc/pwc-ioctl.h
+++ b/drivers/media/video/pwc/pwc-ioctl.h
@@ -2,7 +2,7 @@
#define PWC_IOCTL_H
/* (C) 2001-2004 Nemosoft Unv.
- (C) 2004 Luc Saillard (luc@saillard.org)
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
driver and thus may have bugs that are not present in the original version.
@@ -25,7 +25,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-/* This is pwc-ioctl.h belonging to PWC 8.12.1
+/* This is pwc-ioctl.h belonging to PWC 10.0.10
It contains structures and defines to communicate from user space
directly to the driver.
*/
@@ -51,6 +51,9 @@
... the function
*/
+#include <linux/types.h>
+#include <linux/version.h>
+
/* Enumeration of image sizes */
#define PSZ_SQCIF 0x00
@@ -65,6 +68,8 @@
/* The frame rate is encoded in the video_window.flags parameter using
the upper 16 bits, since some flags are defined nowadays. The following
defines provide a mask and shift to filter out this value.
+ This value can also be passing using the private flag when using v4l2 and
+ VIDIOC_S_FMT ioctl.
In 'Snapshot' mode the camera freezes its automatic exposure and colour
balance controls.
@@ -73,6 +78,8 @@
#define PWC_FPS_MASK 0x00FF0000
#define PWC_FPS_FRMASK 0x003F0000
#define PWC_FPS_SNAPSHOT 0x00400000
+#define PWC_QLT_MASK 0x03000000
+#define PWC_QLT_SHIFT 24
/* structure for transferring x & y coordinates */
@@ -289,4 +296,29 @@ struct pwc_table_init_buffer {
};
#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer)
+/*
+ * This is private command used when communicating with v4l2.
+ * In the future all private ioctl will be remove/replace to
+ * use interface offer by v4l2.
+ */
+
+#define V4L2_CID_PRIVATE_SAVE_USER (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_RESTORE_USER (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_COLOUR_MODE (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_AUTOCONTOUR (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_PRIVATE_CONTOUR (V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_PRIVATE_BACKLIGHT (V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_PRIVATE_FLICKERLESS (V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8)
+
+struct pwc_raw_frame {
+ __le16 type; /* type of the webcam */
+ __le16 vbandlength; /* Size of 4lines compressed (used by the decompressor) */
+ __u8 cmd[4]; /* the four byte of the command (in case of nala,
+ only the first 3 bytes is filled) */
+ __u8 rawframe[0]; /* frame_size = H/4*vbandlength */
+} __attribute__ ((packed));
+
+
#endif
diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c
index fec39cc5a9f1..f4ae83c0cf2b 100644
--- a/drivers/media/video/pwc/pwc-kiara.c
+++ b/drivers/media/video/pwc/pwc-kiara.c
@@ -42,6 +42,8 @@
#include "pwc-kiara.h"
#include "pwc-uncompress.h"
+const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 };
+
const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] =
{
/* SQCIF */
diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/video/pwc/pwc-kiara.h
index 0bdb22547d86..047dad8c15f7 100644
--- a/drivers/media/video/pwc/pwc-kiara.h
+++ b/drivers/media/video/pwc/pwc-kiara.h
@@ -29,6 +29,8 @@
#include <media/pwc-ioctl.h>
+#define PWC_FPS_MAX_KIARA 6
+
struct Kiara_table_entry
{
char alternate; /* USB alternate interface */
@@ -37,8 +39,9 @@ struct Kiara_table_entry
unsigned char mode[12]; /* precomputed mode settings for cam */
};
-extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4];
+extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][PWC_FPS_MAX_KIARA][4];
extern const unsigned int KiaraRomTable[8][2][16][8];
+extern const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA];
#endif
diff --git a/drivers/media/video/pwc/pwc-timon.c b/drivers/media/video/pwc/pwc-timon.c
index be65bdcd195b..c56c174b161c 100644
--- a/drivers/media/video/pwc/pwc-timon.c
+++ b/drivers/media/video/pwc/pwc-timon.c
@@ -40,7 +40,9 @@
#include "pwc-timon.h"
-const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] =
+const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON] = { 5, 10, 15, 20, 25, 30 };
+
+const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4] =
{
/* SQCIF */
{
diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/video/pwc/pwc-timon.h
index eef9e2cd4320..a6e22224c95f 100644
--- a/drivers/media/video/pwc/pwc-timon.h
+++ b/drivers/media/video/pwc/pwc-timon.h
@@ -44,6 +44,8 @@
#include <media/pwc-ioctl.h>
+#define PWC_FPS_MAX_TIMON 6
+
struct Timon_table_entry
{
char alternate; /* USB alternate interface */
@@ -52,9 +54,9 @@ struct Timon_table_entry
unsigned char mode[13]; /* precomputed mode settings for cam */
};
-extern const struct Timon_table_entry Timon_table[PSZ_MAX][6][4];
+extern const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4];
extern const unsigned int TimonRomTable [16][2][16][8];
-
+extern const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON];
#endif
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index d5e6bc850643..32fbe1ae6251 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -1168,7 +1168,7 @@ int pwc_video_do_ioctl(struct inode *inode, struct file *file,
buf->sequence = 0;
buf->memory = V4L2_MEMORY_MMAP;
buf->m.offset = pdev->fill_image * pdev->len_per_image;
- buf->length = buf->bytesused;
+ buf->length = pdev->len_per_image;
pwc_next_image(pdev);
PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index);
@@ -1193,6 +1193,64 @@ int pwc_video_do_ioctl(struct inode *inode, struct file *file,
return 0;
}
+ case VIDIOC_ENUM_FRAMESIZES:
+ {
+ struct v4l2_frmsizeenum *fsize = arg;
+ unsigned int i = 0, index = fsize->index;
+
+ if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
+ for (i = 0; i < PSZ_MAX; i++) {
+ if (pdev->image_mask & (1UL << i)) {
+ if (!index--) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = pwc_image_sizes[i].x;
+ fsize->discrete.height = pwc_image_sizes[i].y;
+ return 0;
+ }
+ }
+ }
+ } else if (fsize->index == 0 &&
+ ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
+ (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = pdev->abs_max.x;
+ fsize->discrete.height = pdev->abs_max.y;
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ {
+ struct v4l2_frmivalenum *fival = arg;
+ int size = -1;
+ unsigned int i;
+
+ for (i = 0; i < PSZ_MAX; i++) {
+ if (pwc_image_sizes[i].x == fival->width &&
+ pwc_image_sizes[i].y == fival->height) {
+ size = i;
+ break;
+ }
+ }
+
+ /* TODO: Support raw format */
+ if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) {
+ return -EINVAL;
+ }
+
+ i = pwc_get_fps(pdev, fival->index, size);
+ if (!i)
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator = i;
+
+ return 0;
+ }
+
default:
return pwc_ioctl(pdev, cmd, arg);
} /* ..switch */
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index e778a2b8c280..acbb9312960a 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -44,7 +44,7 @@
#define PWC_MINOR 0
#define PWC_EXTRAMINOR 12
#define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR)
-#define PWC_VERSION "10.0.12"
+#define PWC_VERSION "10.0.13"
#define PWC_NAME "pwc"
#define PFX PWC_NAME ": "
@@ -85,7 +85,7 @@
#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args)
#define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args)
-#else /* if ! CONFIG_PWC_DEBUG */
+#else /* if ! CONFIG_USB_PWC_DEBUG */
#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args)
#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args)
@@ -287,6 +287,7 @@ void pwc_construct(struct pwc_device *pdev);
/** Functions in pwc-ctrl.c */
/* Request a certain video mode. Returns < 0 if not possible */
extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot);
+extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size);
/* Calculate the number of bytes per image (not frame) */
extern int pwc_mpt_reset(struct pwc_device *pdev, int flags);
extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 4d5bbd859de1..2d18f0069821 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -45,6 +45,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#include <media/saa7115.h>
#include <asm/div64.h>
@@ -80,7 +81,7 @@ struct saa711x_state {
int sat;
int width;
int height;
- enum v4l2_chip_ident ident;
+ u32 ident;
u32 audclk_freq;
u32 crystal_freq;
u8 ucgc;
@@ -1232,7 +1233,6 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct saa711x_state *state = i2c_get_clientdata(client);
- int *iarg = arg;
/* ioctls to allow direct access to the saa7115 registers for testing */
switch (cmd) {
@@ -1437,9 +1437,8 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar
}
#endif
- case VIDIOC_INT_G_CHIP_IDENT:
- *iarg = state->ident;
- break;
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
default:
return -EINVAL;
@@ -1487,6 +1486,7 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
if (memcmp(name, "1f711", 5)) {
v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
address << 1, name);
+ kfree(client);
return 0;
}
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index 654863db1591..9f986930490f 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -54,6 +54,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#include <media/saa7127.h>
static int debug = 0;
@@ -234,7 +235,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = {
struct saa7127_state {
v4l2_std_id std;
- enum v4l2_chip_ident ident;
+ u32 ident;
enum saa7127_input_type input_type;
enum saa7127_output_type output_type;
int video_enable;
@@ -550,12 +551,12 @@ static int saa7127_command(struct i2c_client *client,
struct v4l2_routing *route = arg;
switch (cmd) {
- case VIDIOC_S_STD:
+ case VIDIOC_INT_S_STD_OUTPUT:
if (state->std == *(v4l2_std_id *)arg)
break;
return saa7127_set_std(client, *(v4l2_std_id *)arg);
- case VIDIOC_G_STD:
+ case VIDIOC_INT_G_STD_OUTPUT:
*(v4l2_std_id *)arg = state->std;
break;
@@ -650,9 +651,8 @@ static int saa7127_command(struct i2c_client *client,
break;
}
- case VIDIOC_INT_G_CHIP_IDENT:
- *(enum v4l2_chip_ident *)arg = state->ident;
- break;
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
default:
return -EINVAL;
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index 59da79ce2efd..309dca368f4a 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -46,6 +46,7 @@ config VIDEO_SAA7134_DVB
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_TDA10086 if !DVB_FE_CUSTOMISE
select DVB_TDA826X if !DVB_FE_CUSTOMISE
+ select DVB_TDA827X if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB cards based on the
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 89f32107f46b..4ea479baee74 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -1543,12 +1543,12 @@ struct saa7134_board saa7134_boards[] = {
},{
.name = name_comp1,
.vmux = 0,
- .amux = LINE2,
+ .amux = LINE1,
.gpio = 0x02,
},{
.name = name_svideo,
.vmux = 6,
- .amux = LINE2,
+ .amux = LINE1,
.gpio = 0x02,
}},
.radio = {
@@ -1778,17 +1778,19 @@ struct saa7134_board saa7134_boards[] = {
[SAA7134_BOARD_FLYDVBTDUO] = {
/* LifeView FlyDVB-T DUO */
/* "Nico Sabbi <nsabbi@tiscali.it> Hartmut Hackmann hartmut.hackmann@t-online.de*/
- .name = "LifeView FlyDVB-T DUO",
+ .name = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo",
.audio_clock = 0x00200000,
.tuner_type = TUNER_PHILIPS_TDA8290,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00200000,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
.name = name_tv,
.vmux = 1,
.amux = TV,
+ .gpio = 0x200000, /* GPIO21=High for TV input */
.tv = 1,
},{
.name = name_comp1, /* Composite signal on S-Video input */
@@ -1803,6 +1805,11 @@ struct saa7134_board saa7134_boards[] = {
.vmux = 8,
.amux = LINE2,
}},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
+ },
},
[SAA7134_BOARD_PHILIPS_TOUGH] = {
.name = "Philips TOUGH DVB-T reference design",
@@ -2546,8 +2553,9 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .tuner_config = 0,
.mpeg = SAA7134_MPEG_DVB,
- .gpiomask = 1 << 21,
+ .gpiomask = 0x0200000,
.inputs = {{
.name = name_tv,
.vmux = 1,
@@ -2624,7 +2632,7 @@ struct saa7134_board saa7134_boards[] = {
}},
.radio = {
.name = name_radio,
- .amux = LINE1,
+ .amux = TV,
.gpio = 0x0200000,
},
},
@@ -3043,6 +3051,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .tuner_config = 1,
.mpeg = SAA7134_MPEG_DVB,
.gpiomask = 0x000200000,
.inputs = {{
@@ -3289,6 +3298,115 @@ struct saa7134_board saa7134_boards[] = {
.amux = LINE1,
}},
},
+ [SAA7134_BOARD_PHILIPS_TIGER_S] = {
+ .name = "Philips Tiger - S Reference design",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_M102] = {
+ .name = "Avermedia M102",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 1<<21,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_ASUS_P7131_4871] = {
+ .name = "ASUS P7131 4871",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0200000,
+ }},
+ },
+ [SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = {
+ .name = "ASUSTeK P7131 Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 1 << 21,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0000000,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -3914,7 +4032,7 @@ struct pci_device_id saa7134_pci_tbl[] = {
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x1043,
.subdevice = 0x4876,
- .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+ .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
@@ -3958,6 +4076,30 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x1175,
.driver_data = SAA7134_BOARD_CINERGY_HT_PCI,
},{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf31e,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M102,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x4E42, /* MSI */
+ .subdevice = 0x0306, /* TV@nywhere DUO */
+ .driver_data = SAA7134_BOARD_FLYDVBTDUO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4871,
+ .driver_data = SAA7134_BOARD_ASUS_P7131_4871,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4857,
+ .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+ },{
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -3971,7 +4113,6 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0,
.driver_data = SAA7134_BOARD_NOAUTO,
},{
-
/* --- default catch --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
@@ -4063,6 +4204,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
case SAA7134_BOARD_FLYDVBT_LR301:
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
case SAA7134_BOARD_FLYDVBTDUO:
case SAA7134_BOARD_PROTEUS_2309:
case SAA7134_BOARD_AVERMEDIA_A16AR:
@@ -4103,8 +4245,8 @@ int saa7134_board_init1(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
- saa_writeb(SAA7134_GPIO_GPMODE3, 0x08);
- saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00);
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000);
break;
case SAA7134_BOARD_AVERMEDIA_CARDBUS:
/* power-up tuner chip */
@@ -4137,6 +4279,11 @@ int saa7134_board_init1(struct saa7134_dev *dev)
"%s: Dual decoder functionality is disabled for now, use the other chip.\n",
dev->name,card(dev).name,dev->name,dev->name);
break;
+ case SAA7134_BOARD_AVERMEDIA_M102:
+ /* enable tuner */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd);
+ break;
}
return 0;
}
@@ -4146,6 +4293,9 @@ int saa7134_board_init2(struct saa7134_dev *dev)
{
unsigned char buf;
int board;
+ struct tuner_setup tun_setup;
+ tun_setup.config = 0;
+ tun_setup.tuner_callback = saa7134_tuner_callback;
switch (dev->board) {
case SAA7134_BOARD_BMK_MPEX_NOTUNER:
@@ -4162,8 +4312,6 @@ int saa7134_board_init2(struct saa7134_dev *dev)
dev->tuner_type = saa7134_boards[dev->board].tuner_type;
if (TUNER_ABSENT != dev->tuner_type) {
- struct tuner_setup tun_setup;
-
tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
tun_setup.type = dev->tuner_type;
tun_setup.addr = ADDR_UNSET;
@@ -4173,7 +4321,6 @@ int saa7134_board_init2(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_MD7134:
{
- struct tuner_setup tun_setup;
u8 subaddr;
u8 data[3];
int ret, tuner_t;
@@ -4245,7 +4392,6 @@ int saa7134_board_init2(struct saa7134_dev *dev)
* the channel decoder. We have to make it transparent to find it
*/
{
- struct tuner_setup tun_setup;
u8 data[] = { 0x07, 0x02};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
@@ -4258,16 +4404,38 @@ int saa7134_board_init2(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_PHILIPS_TIGER:
+ case SAA7134_BOARD_PHILIPS_TIGER_S:
+ {
+ u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+ if(dev->autodetected && (dev->eedata[0x49] == 0x50)) {
+ dev->board = SAA7134_BOARD_PHILIPS_TIGER_S;
+ printk(KERN_INFO "%s: Reconfigured board as %s\n",
+ dev->name, saa7134_boards[dev->board].name);
+ }
+ if(dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
+ tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
+ tun_setup.type = TUNER_PHILIPS_TDA8290;
+ tun_setup.addr = 0x4b;
+ tun_setup.config = 2;
+
+ saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup);
+ data[2] = 0x68;
+ }
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ }
+ break;
case SAA7134_BOARD_PINNACLE_PCTV_310i:
case SAA7134_BOARD_TEVION_DVBT_220RF:
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
case SAA7134_BOARD_MEDION_MD8800_QUADRO:
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
/* this is a hybrid board, initialize to analog mode
* and configure firmware eeprom address
*/
{
- u8 data[] = { 0x3c, 0x33, 0x68};
+ u8 data[] = { 0x3c, 0x33, 0x60};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
}
@@ -4281,18 +4449,18 @@ int saa7134_board_init2(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
- /* make the tda10046 find its eeprom */
+ /* initialize analog mode */
{
- u8 data[] = { 0x3c, 0x33, 0x62};
+ u8 data[] = { 0x3c, 0x33, 0x6a};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
}
break;
case SAA7134_BOARD_CINERGY_HT_PCMCIA:
case SAA7134_BOARD_CINERGY_HT_PCI:
- /* make the tda10046 find its eeprom */
+ /* initialize analog mode */
{
- u8 data[] = { 0x3c, 0x33, 0x60};
+ 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);
}
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index ed038fff3b4f..25f84701a8e8 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -117,6 +117,64 @@ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg)
dev->name, mode, (~mode) & status, mode & status, msg);
}
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value)
+{
+ u32 index, bitval;
+
+ index = 1 << bit_no;
+ switch (value) {
+ case 0: /* static value */
+ case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value);
+ /* turn sync mode off if necessary */
+ if (index & 0x00c00000)
+ saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00);
+ if (value)
+ bitval = index;
+ else
+ bitval = 0;
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval);
+ break;
+ case 3: /* tristate */
+ dprintk("setting GPIO%d to tristate\n", bit_no);
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0);
+ break;
+ }
+}
+
+int saa7134_tuner_callback(void *ptr, int command, int arg)
+{
+ u8 sync_control;
+ struct saa7134_dev *dev = ptr;
+
+ switch (dev->tuner_type) {
+ case TUNER_PHILIPS_TDA8290:
+ switch (command) {
+ case 0: /* switch LNA gain through GPIO 22*/
+ saa7134_set_gpio(dev, 22, arg) ;
+ break;
+ case 1: /* vsync output at GPIO22. 50 / 60Hz */
+ dprintk("setting GPIO22 to vsync %d\n", arg);
+ saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80);
+ saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03);
+ if (arg == 1)
+ sync_control = 11;
+ else
+ sync_control = 17;
+ saa_writeb(SAA7134_VGATE_START, sync_control);
+ saa_writeb(SAA7134_VGATE_STOP, sync_control + 1);
+ saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
/* ------------------------------------------------------------------ */
@@ -124,55 +182,28 @@ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg)
/* delayed request_module */
#if defined(CONFIG_MODULES) && defined(MODULE)
-static int need_empress;
-static int need_dvb;
-static int need_alsa;
-static int need_oss;
-static int pending_call(struct notifier_block *self, unsigned long state,
- void *module)
-{
- if (module != THIS_MODULE || state != MODULE_STATE_LIVE)
- return NOTIFY_DONE;
- if (need_empress)
+static void request_module_async(struct work_struct *work){
+ struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk);
+ if (card_is_empress(dev))
request_module("saa7134-empress");
- if (need_dvb)
+ if (card_is_dvb(dev))
request_module("saa7134-dvb");
- if (need_alsa)
+ if (alsa)
request_module("saa7134-alsa");
- if (need_oss)
+ if (oss)
request_module("saa7134-oss");
- return NOTIFY_DONE;
}
-static int pending_registered;
-static struct notifier_block pending_notifier = {
- .notifier_call = pending_call,
-};
-
-static void request_module_depend(char *name, int *flag)
+static void request_submodules(struct saa7134_dev *dev)
{
- int err;
- switch (THIS_MODULE->state) {
- case MODULE_STATE_COMING:
- if (!pending_registered) {
- err = register_module_notifier(&pending_notifier);
- pending_registered = 1;
- }
- *flag = 1;
- break;
- case MODULE_STATE_LIVE:
- request_module(name);
- break;
- default:
- /* nothing */;
- break;
- }
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
}
#else
-#define request_module_depend(name,flag)
+#define request_submodules(dev)
#endif /* CONFIG_MODULES */
/* ------------------------------------------------------------------ */
@@ -703,7 +734,6 @@ static int saa7134_hwfini(struct saa7134_dev *dev)
saa7134_ts_fini(dev);
saa7134_input_fini(dev);
saa7134_vbi_fini(dev);
- saa7134_video_fini(dev);
saa7134_tvaudio_fini(dev);
return 0;
}
@@ -944,18 +974,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
request_module("tuner");
if (card_is_empress(dev)) {
request_module("saa6752hs");
- request_module_depend("saa7134-empress",&need_empress);
}
- if (card_is_dvb(dev))
- request_module_depend("saa7134-dvb",&need_dvb);
-
-
- if (alsa)
- request_module_depend("saa7134-alsa",&need_alsa);
-
- if (oss)
- request_module_depend("saa7134-oss",&need_oss);
+ request_submodules(dev);
v4l2_prio_init(&dev->prio);
@@ -1013,6 +1034,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
saa7134_dmasound_init(dev);
}
+ if (TUNER_ABSENT != dev->tuner_type)
+ saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
return 0;
fail4:
@@ -1152,10 +1176,6 @@ static int saa7134_init(void)
static void saa7134_fini(void)
{
-#if defined(CONFIG_MODULES) && defined(MODULE)
- if (pending_registered)
- unregister_module_notifier(&pending_notifier);
-#endif /* CONFIG_MODULES */
pci_unregister_driver(&saa7134_pci_driver);
}
@@ -1164,6 +1184,7 @@ module_exit(saa7134_fini);
/* ----------------------------------------------------------- */
+EXPORT_SYMBOL(saa7134_set_gpio);
EXPORT_SYMBOL(saa7134_i2c_call_clients);
EXPORT_SYMBOL(saa7134_devlist);
EXPORT_SYMBOL(saa7134_boards);
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index e3059fd33951..65aec881bbde 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -41,7 +41,9 @@
#include "tda10086.h"
#include "tda826x.h"
+#include "tda827x.h"
#include "isl6421.h"
+
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
@@ -54,7 +56,21 @@ static int use_frontend = 0;
module_param(use_frontend, int, 0644);
MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)");
-/* ------------------------------------------------------------------ */
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off).");
+
+#define dprintk(fmt, arg...) do { if (debug) \
+ printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0)
+
+/* Print a warning */
+#define wprintk(fmt, arg...) \
+ printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg)
+
+/* ------------------------------------------------------------------
+ * mt352 based DVB-T cards
+ */
+
static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
{
u32 ok;
@@ -75,8 +91,7 @@ static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
udelay(10);
ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27);
- printk("%s: %s %s\n", dev->name, __FUNCTION__,
- ok ? "on" : "off");
+ dprintk("%s %s\n", __FUNCTION__, ok ? "on" : "off");
if (!ok)
saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
@@ -96,7 +111,7 @@ static int mt352_pinnacle_init(struct dvb_frontend* fe)
static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 };
struct saa7134_dev *dev= fe->dvb->priv;
- printk("%s: %s called\n",dev->name,__FUNCTION__);
+ dprintk("%s called\n", __FUNCTION__);
mt352_write(fe, clock_config, sizeof(clock_config));
udelay(200);
@@ -185,10 +200,26 @@ static struct mt352_config avermedia_777 = {
.demod_init = mt352_aver777_init,
};
-/* ------------------------------------------------------------------ */
-static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+/* ==================================================================
+ * tda1004x based DVB-T cards, helper functions
+ */
+
+static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
+ const struct firmware **fw, char *name)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ return request_firmware(fw, name, &dev->pci->dev);
+}
+
+/* ------------------------------------------------------------------
+ * these tuners are tu1216, td1316(a)
+ */
+
+static int philips_tda6651_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
{
struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
u8 tuner_buf[4];
struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len =
sizeof(tuner_buf) };
@@ -263,15 +294,20 @@ static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+ if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) {
+ wprintk("could not write to tuner at addr: 0x%02x\n",
+ addr << 1);
return -EIO;
+ }
msleep(1);
return 0;
}
-static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe)
+static int philips_tu1216_init(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
@@ -287,46 +323,17 @@ static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe)
/* ------------------------------------------------------------------ */
-static int philips_tu1216_tuner_60_init(struct dvb_frontend *fe)
-{
- return philips_tda6651_pll_init(0x60, fe);
-}
-
-static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
- return philips_tda6651_pll_set(0x60, fe, params);
-}
-
-static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
- const struct firmware **fw, char *name)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- return request_firmware(fw, name, &dev->pci->dev);
-}
-
static struct tda1004x_config philips_tu1216_60_config = {
-
.demod_address = 0x8,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_4M,
.agc_config = TDA10046_AGC_DEFAULT,
.if_freq = TDA10046_FREQ_3617,
- .request_firmware = philips_tda1004x_request_firmware,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
-
-static int philips_tu1216_tuner_61_init(struct dvb_frontend *fe)
-{
- return philips_tda6651_pll_init(0x61, fe);
-}
-
-static int philips_tu1216_tuner_61_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
- return philips_tda6651_pll_set(0x61, fe, params);
-}
-
static struct tda1004x_config philips_tu1216_61_config = {
.demod_address = 0x8,
@@ -335,7 +342,8 @@ static struct tda1004x_config philips_tu1216_61_config = {
.xtal_freq = TDA10046_XTAL_4M,
.agc_config = TDA10046_AGC_DEFAULT,
.if_freq = TDA10046_FREQ_3617,
- .request_firmware = philips_tda1004x_request_firmware,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
};
/* ------------------------------------------------------------------ */
@@ -343,24 +351,42 @@ static struct tda1004x_config philips_tu1216_61_config = {
static int philips_td1316_tuner_init(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
- struct i2c_msg init_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) };
+ struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
/* setup PLL configuration */
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
return -EIO;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
return 0;
}
static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
{
- return philips_tda6651_pll_set(0x61, fe, params);
+ return philips_tda6651_pll_set(fe, params);
}
+static int philips_td1316_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 };
+ struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+ /* switch the tuner to analog mode */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
static int philips_europa_tuner_init(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
@@ -380,18 +406,14 @@ static int philips_europa_tuner_init(struct dvb_frontend *fe)
static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
- /* this message actually turns the tuner back to analog mode */
- static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 };
- struct i2c_msg analog_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) };
- i2c_transfer(&dev->i2c_adap, &analog_msg, 1);
- msleep(1);
+ static u8 msg[] = { 0x00, 0x14 };
+ struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+ if (philips_td1316_tuner_sleep(fe))
+ return -EIO;
/* switch the board to analog mode */
- analog_msg.addr = 0x43;
- analog_msg.len = 0x02;
- msg[0] = 0x00;
- msg[1] = 0x14;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
i2c_transfer(&dev->i2c_adap, &analog_msg, 1);
@@ -416,7 +438,8 @@ static struct tda1004x_config philips_europa_config = {
.xtal_freq = TDA10046_XTAL_4M,
.agc_config = TDA10046_AGC_IFO_AUTO_POS,
.if_freq = TDA10046_FREQ_052,
- .request_firmware = NULL,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
};
/* ------------------------------------------------------------------ */
@@ -424,9 +447,11 @@ static struct tda1004x_config philips_europa_config = {
static int philips_fmd1216_tuner_init(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
/* this message is to set up ATC and ALC */
static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 };
- struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
+ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
@@ -440,9 +465,11 @@ static int philips_fmd1216_tuner_init(struct dvb_frontend *fe)
static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
/* this message actually turns the tuner back to analog mode */
- static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 };
- struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
+ u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 };
+ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
@@ -460,8 +487,10 @@ static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe)
static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
{
struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
u8 tuner_buf[4];
- struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = tuner_buf,.len =
+ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len =
sizeof(tuner_buf) };
int tuner_frequency = 0;
int divider = 0;
@@ -536,8 +565,11 @@ static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+ if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) {
+ wprintk("could not write to tuner at addr: 0x%02x\n",
+ addr << 1);
return -EIO;
+ }
return 0;
}
@@ -548,582 +580,365 @@ static struct tda1004x_config medion_cardbus = {
.xtal_freq = TDA10046_XTAL_16M,
.agc_config = TDA10046_AGC_IFO_AUTO_NEG,
.if_freq = TDA10046_FREQ_3613,
- .request_firmware = NULL,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
-
-struct tda827x_data {
- u32 lomax;
- u8 spd;
- u8 bs;
- u8 bp;
- u8 cp;
- u8 gc3;
- u8 div1p5;
-};
-
-static struct tda827x_data tda827x_dvbt[] = {
- { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
- { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
- { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
- { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
- { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
- { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1},
- { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0},
- { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
- { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1},
- { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
- { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1},
- { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0},
- { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
- { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
- { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
- { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0},
- { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0},
- { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0}
-};
-
-static int philips_tda827x_tuner_init(struct dvb_frontend *fe)
-{
- return 0;
-}
+/* ------------------------------------------------------------------
+ * tda 1004x based cards with philips silicon tuner
+ */
-static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+static void philips_tda827x_lna_gain(struct dvb_frontend *fe, int high)
{
struct saa7134_dev *dev = fe->dvb->priv;
- u8 tuner_buf[14];
-
- struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,
- .len = sizeof(tuner_buf) };
- int i, tuner_freq, if_freq;
- u32 N;
- switch (params->u.ofdm.bandwidth) {
- case BANDWIDTH_6_MHZ:
- if_freq = 4000000;
- break;
- case BANDWIDTH_7_MHZ:
- if_freq = 4500000;
- break;
- default: /* 8 MHz or Auto */
- if_freq = 5000000;
- break;
- }
- tuner_freq = params->frequency + if_freq;
-
- i = 0;
- while (tda827x_dvbt[i].lomax < tuner_freq) {
- if(tda827x_dvbt[i + 1].lomax == 0)
- break;
- i++;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->i2c_gate;
+ u8 config = state->config->tuner_config;
+ u8 GP00_CF[] = {0x20, 0x01};
+ u8 GP00_LEV[] = {0x22, 0x00};
+
+ struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = GP00_CF, .len = 2};
+ if (config) {
+ if (high) {
+ dprintk("setting LNA to high gain\n");
+ } else {
+ dprintk("setting LNA to low gain\n");
+ }
}
-
- N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2);
- tuner_buf[0] = 0;
- tuner_buf[1] = (N>>8) | 0x40;
- tuner_buf[2] = N & 0xff;
- tuner_buf[3] = 0;
- tuner_buf[4] = 0x52;
- tuner_buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) +
- (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp;
- tuner_buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f;
- tuner_buf[7] = 0xbf;
- tuner_buf[8] = 0x2a;
- tuner_buf[9] = 0x05;
- tuner_buf[10] = 0xff;
- tuner_buf[11] = 0x00;
- tuner_buf[12] = 0x00;
- tuner_buf[13] = 0x40;
-
- tuner_msg.len = 14;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
- return -EIO;
-
- msleep(500);
- /* correct CP value */
- tuner_buf[0] = 0x30;
- tuner_buf[1] = 0x50 + tda827x_dvbt[i].cp;
- tuner_msg.len = 2;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
-
- return 0;
-}
-
-static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- static u8 tda827x_sleep[] = { 0x30, 0xd0};
- struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tda827x_sleep,
- .len = sizeof(tda827x_sleep) };
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
- return 0;
-}
-
-static struct tda1004x_config tda827x_lifeview_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP11,
- .if_freq = TDA10046_FREQ_045,
- .request_firmware = NULL,
-};
-
-/* ------------------------------------------------------------------ */
-
-struct tda827xa_data {
- u32 lomax;
- u8 svco;
- u8 spd;
- u8 scr;
- u8 sbs;
- u8 gc3;
-};
-
-static struct tda827xa_data tda827xa_dvbt[] = {
- { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1},
- { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
- { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
- { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
- { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1},
- { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
- { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
- { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
- { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
- { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
- { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
- { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
- { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
- { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
- { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
- { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1},
- { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
- { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
- { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
- { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
- { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
- { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
- { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
- { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
- { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
- { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0},
- { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}};
-
-
-static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- u8 tuner_buf[14];
- unsigned char reg2[2];
-
- struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = tuner_buf};
- int i, tuner_freq, if_freq;
- u32 N;
-
- switch (params->u.ofdm.bandwidth) {
- case BANDWIDTH_6_MHZ:
- if_freq = 4000000;
+ switch (config) {
+ case 0: /* no LNA */
break;
- case BANDWIDTH_7_MHZ:
- if_freq = 4500000;
+ case 1: /* switch is GPIO 0 of tda8290 */
+ case 2:
+ /* turn Vsync off */
+ saa7134_set_gpio(dev, 22, 0);
+ GP00_LEV[1] = high ? 0 : 1;
+ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
+ wprintk("could not access tda8290 at addr: 0x%02x\n",
+ addr << 1);
+ return;
+ }
+ msg.buf = GP00_LEV;
+ if (config == 2)
+ GP00_LEV[1] = high ? 1 : 0;
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
break;
- default: /* 8 MHz or Auto */
- if_freq = 5000000;
+ case 3: /* switch with GPIO of saa713x */
+ saa7134_set_gpio(dev, 22, high);
break;
}
- tuner_freq = params->frequency + if_freq;
-
- i = 0;
- while (tda827xa_dvbt[i].lomax < tuner_freq) {
- if(tda827xa_dvbt[i + 1].lomax == 0)
- break;
- i++;
- }
-
- N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd;
- tuner_buf[0] = 0; // subaddress
- tuner_buf[1] = N >> 8;
- tuner_buf[2] = N & 0xff;
- tuner_buf[3] = 0;
- tuner_buf[4] = 0x16;
- tuner_buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) +
- tda827xa_dvbt[i].sbs;
- tuner_buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4);
- tuner_buf[7] = 0x0c;
- tuner_buf[8] = 0x06;
- tuner_buf[9] = 0x24;
- tuner_buf[10] = 0xff;
- tuner_buf[11] = 0x60;
- tuner_buf[12] = 0x00;
- tuner_buf[13] = 0x39; // lpsel
- msg.len = 14;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
- return -EIO;
-
- msg.buf= reg2;
- msg.len = 2;
- reg2[0] = 0x60;
- reg2[1] = 0x3c;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &msg, 1);
-
- reg2[0] = 0xa0;
- reg2[1] = 0x40;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &msg, 1);
-
- msleep(2);
- /* correct CP value */
- reg2[0] = 0x30;
- reg2[1] = 0x10 + tda827xa_dvbt[i].scr;
- msg.len = 2;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &msg, 1);
-
- msleep(550);
- reg2[0] = 0x50;
- reg2[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &msg, 1);
-
- return 0;
-
}
-static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe)
+static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable)
{
- struct saa7134_dev *dev = fe->dvb->priv;
- static u8 tda827xa_sleep[] = { 0x30, 0x90};
- struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tda827xa_sleep,
- .len = sizeof(tda827xa_sleep) };
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- return 0;
-}
+ struct tda1004x_state *state = fe->demodulator_priv;
-/* ------------------------------------------------------------------ */
-
-static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
+ u8 addr = state->config->i2c_gate;
static u8 tda8290_close[] = { 0x21, 0xc0};
static u8 tda8290_open[] = { 0x21, 0x80};
- struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2};
+ struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2};
if (enable) {
tda8290_msg.buf = tda8290_close;
} else {
tda8290_msg.buf = tda8290_open;
}
- if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1)
+ if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) {
+ struct saa7134_dev *dev = fe->dvb->priv;
+ wprintk("could not access tda8290 I2C gate\n");
return -EIO;
+ }
msleep(20);
return 0;
}
/* ------------------------------------------------------------------ */
-static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+static int philips_tda827x_tuner_init(struct dvb_frontend *fe)
{
- int ret;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
- ret = philips_tda827xa_pll_set(0x61, fe, params);
- if (ret != 0)
- return ret;
+ switch (state->config->antenna_switch) {
+ case 0: break;
+ case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+ saa7134_set_gpio(dev, 21, 0);
+ break;
+ case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+ saa7134_set_gpio(dev, 21, 1);
+ break;
+ }
return 0;
}
-static int philips_tiger_tuner_init(struct dvb_frontend *fe)
+static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe)
{
struct saa7134_dev *dev = fe->dvb->priv;
- static u8 data[] = { 0x3c, 0x33, 0x6a};
- struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+ struct tda1004x_state *state = fe->demodulator_priv;
- if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
- return -EIO;
+ switch (state->config->antenna_switch) {
+ case 0: break;
+ case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+ saa7134_set_gpio(dev, 21, 1);
+ break;
+ case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+ saa7134_set_gpio(dev, 21, 0);
+ break;
+ }
return 0;
}
-static int philips_tiger_tuner_sleep(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)};
+static struct tda827x_config tda827x_cfg = {
+ .lna_gain = philips_tda827x_lna_gain,
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep
+};
- i2c_transfer(&dev->i2c_adap, &msg, 1);
- philips_tda827xa_tuner_sleep( 0x61, fe);
- return 0;
+static void configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *tda_conf)
+{
+ dev->dvb.frontend = dvb_attach(tda10046_attach, tda_conf, &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ if (tda_conf->i2c_gate)
+ dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+ if (dvb_attach(tda827x_attach, dev->dvb.frontend, tda_conf->tuner_address,
+ &dev->i2c_adap,&tda827x_cfg) == NULL) {
+ wprintk("no tda827x tuner found at addr: %02x\n",
+ tda_conf->tuner_address);
+ }
+ }
}
-static struct tda1004x_config philips_tiger_config = {
+/* ------------------------------------------------------------------ */
+static struct tda1004x_config tda827x_lifeview_config = {
.demod_address = 0x08,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = NULL,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
-
-static int cinergy_ht_tuner_init(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- static u8 data[] = { 0x3c, 0x33, 0x62};
- struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
- if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
- return -EIO;
- return 0;
-}
-
-static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- static u8 data[] = { 0x3c, 0x33, 0x60};
- 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 struct tda1004x_config philips_tiger_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .tuner_config = 0,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
static struct tda1004x_config cinergy_ht_config = {
.demod_address = 0x08,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP01,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = NULL,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .tuner_config = 0,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
+static struct tda1004x_config cinergy_ht_pci_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x60,
+ .tuner_config = 0,
+ .request_firmware = philips_tda1004x_request_firmware
+};
-static struct tda1004x_config pinnacle_pctv_310i_config = {
+static struct tda1004x_config philips_tiger_s_config = {
.demod_address = 0x08,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = philips_tda1004x_request_firmware,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .tuner_config = 2,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
+static struct tda1004x_config pinnacle_pctv_310i_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .tuner_config = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
static struct tda1004x_config hauppauge_hvr_1110_config = {
.demod_address = 0x08,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = philips_tda1004x_request_firmware,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
-
static struct tda1004x_config asus_p7131_dual_config = {
.demod_address = 0x08,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = philips_tda1004x_request_firmware,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .tuner_config = 0,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
};
-static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- static u8 data[] = { 0x3c, 0x33, 0x6a};
- struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
-
- if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
- return -EIO;
- /* make sure the DVB-T antenna input is set */
- saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000);
- return 0;
-}
-
-static int asus_p7131_dual_tuner_sleep(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);
- /* reset antenna inputs for analog usage */
- saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000);
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int lifeview_trio_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
- int ret;
-
- ret = philips_tda827xa_pll_set(0x60, fe, params);
- return ret;
-}
-
-static int lifeview_trio_tuner_sleep(struct dvb_frontend *fe)
-{
- philips_tda827xa_tuner_sleep(0x60, fe);
- return 0;
-}
-
static struct tda1004x_config lifeview_trio_config = {
.demod_address = 0x09,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP00,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP00_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = NULL,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
-
-static int ads_duo_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
- int ret;
-
- ret = philips_tda827xa_pll_set(0x61, fe, params);
- return ret;
-}
-
-static int ads_duo_tuner_init(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- /* route TDA8275a AGC input to the channel decoder */
- saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x60);
- return 0;
-}
-
-static int ads_duo_tuner_sleep(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- /* route TDA8275a AGC input to the analog IF chip*/
- saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x20);
- philips_tda827xa_tuner_sleep( 0x61, fe);
- return 0;
-}
-
-static struct tda1004x_config ads_tech_duo_config = {
+static struct tda1004x_config tevion_dvbt220rf_config = {
.demod_address = 0x08,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP00,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = NULL,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
-
-static int tevion_dvb220rf_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
-{
- int ret;
- ret = philips_tda827xa_pll_set(0x60, fe, params);
- return ret;
-}
-
-static int tevion_dvb220rf_tuner_sleep(struct dvb_frontend *fe)
-{
- philips_tda827xa_tuner_sleep( 0x61, fe);
- return 0;
-}
+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,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x60,
+ .tuner_config = 0,
+ .request_firmware = philips_tda1004x_request_firmware
+};
-static struct tda1004x_config tevion_dvbt220rf_config = {
+static struct tda1004x_config asus_p7131_4871_config = {
.demod_address = 0x08,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = NULL,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .tuner_config = 2,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
};
-/* ------------------------------------------------------------------ */
+static struct tda1004x_config asus_p7131_hybrid_lna_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .tuner_config = 2,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+/* ------------------------------------------------------------------
+ * special case: this card uses saa713x GPIO22 for the mode switch
+ */
-static int md8800_dvbt_analog_mode(struct dvb_frontend *fe)
+static int ads_duo_tuner_init(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);
+ philips_tda827x_tuner_init(fe);
+ /* route TDA8275a AGC input to the channel decoder */
+ saa7134_set_gpio(dev, 22, 1);
return 0;
}
-static int md8800_dvbt_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+static int ads_duo_tuner_sleep(struct dvb_frontend *fe)
{
- 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;
+ /* route TDA8275a AGC input to the analog IF chip*/
+ saa7134_set_gpio(dev, 22, 0);
+ philips_tda827x_tuner_sleep(fe);
+ return 0;
}
-static struct tda1004x_config md8800_dvbt_config = {
+static struct tda827x_config ads_duo_cfg = {
+ .lna_gain = philips_tda827x_lna_gain,
+ .init = ads_duo_tuner_init,
+ .sleep = ads_duo_tuner_sleep
+};
+
+static struct tda1004x_config ads_tech_duo_config = {
.demod_address = 0x08,
.invert = 1,
.invert_oclk = 0,
.xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X_GP11,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP00_I,
.if_freq = TDA10046_FREQ_045,
- .request_firmware = NULL,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
};
+/* ==================================================================
+ * tda10086 based DVB-S cards, helper functions
+ */
+
static struct tda10086_config flydvbs = {
.demod_address = 0x0e,
.invert = 0,
};
-/* ------------------------------------------------------------------ */
+/* ==================================================================
+ * nxt200x based ATSC cards, helper functions
+ */
static struct nxt200x_config avertvhda180 = {
.demod_address = 0x0a,
@@ -1143,10 +958,13 @@ static struct nxt200x_config kworldatsc110 = {
.set_pll_input = nxt200x_set_pll_input,
};
-/* ------------------------------------------------------------------ */
+/* ==================================================================
+ * Core code
+ */
static int dvb_init(struct saa7134_dev *dev)
{
+ int ret;
/* init struct videobuf_dvb */
dev->ts.nr_bufs = 32;
dev->ts.nr_packets = 32*4;
@@ -1160,7 +978,7 @@ static int dvb_init(struct saa7134_dev *dev)
switch (dev->board) {
case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
- printk("%s: pinnacle 300i dvb setup\n",dev->name);
+ dprintk("pinnacle 300i dvb setup\n");
dev->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i,
&dev->i2c_adap);
if (dev->dvb.frontend) {
@@ -1169,7 +987,7 @@ static int dvb_init(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_AVERMEDIA_777:
case SAA7134_BOARD_AVERMEDIA_A16AR:
- printk("%s: avertv 777 dvb setup\n",dev->name);
+ dprintk("avertv 777 dvb setup\n");
dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777,
&dev->i2c_adap);
if (dev->dvb.frontend) {
@@ -1191,42 +1009,15 @@ static int dvb_init(struct saa7134_dev *dev)
&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;
+ dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+ dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
}
break;
case SAA7134_BOARD_FLYDVBTDUO:
- 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;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params;
- }
- break;
case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
- 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;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params;
- }
+ configure_tda827x_fe(dev, &tda827x_lifeview_config);
break;
case SAA7134_BOARD_PHILIPS_EUROPA:
- 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;
- dev->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
- dev->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
- }
- break;
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
dev->dvb.frontend = dvb_attach(tda10046_attach,
&philips_europa_config,
@@ -1244,125 +1035,61 @@ static int dvb_init(struct saa7134_dev *dev)
&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;
+ dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+ dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
}
break;
case SAA7134_BOARD_PHILIPS_TIGER:
- dev->dvb.frontend = dvb_attach(tda10046_attach,
- &philips_tiger_config,
- &dev->i2c_adap);
- if (dev->dvb.frontend) {
- dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
- dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
- dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
- }
+ configure_tda827x_fe(dev, &philips_tiger_config);
break;
case SAA7134_BOARD_PINNACLE_PCTV_310i:
- dev->dvb.frontend = dvb_attach(tda10046_attach,
- &pinnacle_pctv_310i_config,
- &dev->i2c_adap);
- if (dev->dvb.frontend) {
- dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
- dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
- dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
- }
+ configure_tda827x_fe(dev, &pinnacle_pctv_310i_config);
break;
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
- dev->dvb.frontend = dvb_attach(tda10046_attach,
- &hauppauge_hvr_1110_config,
- &dev->i2c_adap);
- if (dev->dvb.frontend) {
- dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
- dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
- dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
- }
+ configure_tda827x_fe(dev, &hauppauge_hvr_1110_config);
break;
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
- dev->dvb.frontend = dvb_attach(tda10046_attach,
- &asus_p7131_dual_config,
- &dev->i2c_adap);
- if (dev->dvb.frontend) {
- dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
- dev->dvb.frontend->ops.tuner_ops.init = asus_p7131_dual_tuner_init;
- dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
- }
+ configure_tda827x_fe(dev, &asus_p7131_dual_config);
break;
case SAA7134_BOARD_FLYDVBT_LR301:
- 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;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params;
- }
+ configure_tda827x_fe(dev, &tda827x_lifeview_config);
break;
case SAA7134_BOARD_FLYDVB_TRIO:
if(! use_frontend) { //terrestrial
- 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;
- }
+ configure_tda827x_fe(dev, &lifeview_trio_config);
} else { //satellite
dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
if (dev->dvb.frontend) {
if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63,
&dev->i2c_adap, 0) == NULL) {
- printk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__);
+ wprintk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__);
}
if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap,
0x08, 0, 0) == NULL) {
- printk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__);
+ wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__);
}
}
}
break;
case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+ case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
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;
+ if (dvb_attach(tda827x_attach,dev->dvb.frontend,
+ ads_tech_duo_config.tuner_address,
+ &dev->i2c_adap,&ads_duo_cfg) == NULL) {
+ wprintk("no tda827x tuner found at addr: %02x\n",
+ ads_tech_duo_config.tuner_address);
+ }
}
break;
case SAA7134_BOARD_TEVION_DVBT_220RF:
- 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 = 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;
- }
+ configure_tda827x_fe(dev, &tevion_dvbt220rf_config);
break;
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;
- }
+ configure_tda827x_fe(dev, &md8800_dvbt_config);
break;
case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180:
dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180,
@@ -1386,11 +1113,11 @@ static int dvb_init(struct saa7134_dev *dev)
if (dev->dvb.frontend) {
if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60,
&dev->i2c_adap, 0) == NULL) {
- printk("%s: No tda826x found!\n", __FUNCTION__);
+ wprintk("%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__);
+ wprintk("%s: No ISL6421 found!\n", __FUNCTION__);
}
}
break;
@@ -1415,41 +1142,45 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_CINERGY_HT_PCMCIA:
- dev->dvb.frontend = dvb_attach(tda10046_attach,
- &cinergy_ht_config,
- &dev->i2c_adap);
- if (dev->dvb.frontend) {
- dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
- dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init;
- dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep;
- dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
-
- }
+ configure_tda827x_fe(dev, &cinergy_ht_config);
break;
case SAA7134_BOARD_CINERGY_HT_PCI:
- dev->dvb.frontend = dvb_attach(tda10046_attach,
- &cinergy_ht_config,
- &dev->i2c_adap);
- if (dev->dvb.frontend) {
- dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
- dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init;
- dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep;
- dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set;
-
- }
+ configure_tda827x_fe(dev, &cinergy_ht_pci_config);
+ break;
+ case SAA7134_BOARD_PHILIPS_TIGER_S:
+ configure_tda827x_fe(dev, &philips_tiger_s_config);
+ break;
+ case SAA7134_BOARD_ASUS_P7131_4871:
+ configure_tda827x_fe(dev, &asus_p7131_4871_config);
+ break;
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+ configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config);
break;
default:
- printk("%s: Huh? unknown DVB card?\n",dev->name);
+ wprintk("Huh? unknown DVB card?\n");
break;
}
if (NULL == dev->dvb.frontend) {
- printk("%s: frontend initialization failed\n",dev->name);
+ printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
return -1;
}
/* register everything else */
- return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev);
+ ret = videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev);
+
+ /* this sequence is necessary to make the tda1004x load its firmware
+ * and to enter analog mode of hybrid boards
+ */
+ if (!ret) {
+ if (dev->dvb.frontend->ops.init)
+ dev->dvb.frontend->ops.init(dev->dvb.frontend);
+ if (dev->dvb.frontend->ops.sleep)
+ dev->dvb.frontend->ops.sleep(dev->dvb.frontend);
+ if (dev->dvb.frontend->ops.tuner_ops.sleep)
+ dev->dvb.frontend->ops.tuner_ops.sleep(dev->dvb.frontend);
+ }
+ return ret;
}
static int dvb_fini(struct saa7134_dev *dev)
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index cce8da6a4f94..1cb8c709ca90 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -370,6 +370,8 @@ static int attach_inform(struct i2c_client *client)
tun_setup.type = tuner;
tun_setup.addr = saa7134_boards[dev->board].tuner_addr;
+ tun_setup.config = saa7134_boards[dev->board].tuner_config;
+ tun_setup.tuner_callback = saa7134_tuner_callback;
if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) {
@@ -445,7 +447,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
unsigned char buf;
int i,rc;
- for (i = 0; i < 128; i++) {
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
c->addr = i;
rc = i2c_master_recv(c,&buf,0);
if (rc < 0)
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 46c583f1e788..c0de37e3f5c6 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -321,6 +321,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keydown = 0x0040000;
break;
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
ir_codes = ir_codes_asus_pc39;
mask_keydown = 0x0040000;
rc5_gpio = 1;
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index f2cb63053041..9985ded20950 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -26,6 +26,7 @@
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/sort.h>
#include "saa7134-reg.h"
#include "saa7134.h"
@@ -516,14 +517,12 @@ static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int
return 1;
}
-static
-int res_check(struct saa7134_fh *fh, unsigned int bit)
+static int res_check(struct saa7134_fh *fh, unsigned int bit)
{
return (fh->resources & bit);
}
-static
-int res_locked(struct saa7134_dev *dev, unsigned int bit)
+static int res_locked(struct saa7134_dev *dev, unsigned int bit)
{
return (dev->resources & bit);
}
@@ -603,7 +602,14 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40);
saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80);
- saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id);
+ /* only tell the tuner if this is a tv input */
+ if (card_in(dev,dev->ctl_input).tv) {
+ if ((card(dev).tuner_type == TUNER_PHILIPS_TDA8290)
+ && ((card(dev).tuner_config == 1)
+ || (card(dev).tuner_config == 2)))
+ saa7134_set_gpio(dev, 22, 5);
+ saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id);
+ }
}
static void video_mux(struct saa7134_dev *dev, int input)
@@ -732,25 +738,6 @@ struct cliplist {
__u8 disable;
};
-static void sort_cliplist(struct cliplist *cl, int entries)
-{
- struct cliplist swap;
- int i,j,n;
-
- for (i = entries-2; i >= 0; i--) {
- for (n = 0, j = 0; j <= i; j++) {
- if (cl[j].position > cl[j+1].position) {
- swap = cl[j];
- cl[j] = cl[j+1];
- cl[j+1] = swap;
- n++;
- }
- }
- if (0 == n)
- break;
- }
-}
-
static void set_cliplist(struct saa7134_dev *dev, int reg,
struct cliplist *cl, int entries, char *name)
{
@@ -784,15 +771,27 @@ static int clip_range(int val)
return val;
}
+/* Sort into smallest position first order */
+static int cliplist_cmp(const void *a, const void *b)
+{
+ const struct cliplist *cla = a;
+ const struct cliplist *clb = b;
+ if (cla->position < clb->position)
+ return -1;
+ if (cla->position > clb->position)
+ return 1;
+ return 0;
+}
+
static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
int nclips, int interlace)
{
struct cliplist col[16], row[16];
- int cols, rows, i;
+ int cols = 0, rows = 0, i;
int div = interlace ? 2 : 1;
- memset(col,0,sizeof(col)); cols = 0;
- memset(row,0,sizeof(row)); rows = 0;
+ memset(col, 0, sizeof(col));
+ memset(row, 0, sizeof(row));
for (i = 0; i < nclips && i < 8; i++) {
col[cols].position = clip_range(clips[i].c.left);
col[cols].enable = (1 << i);
@@ -808,8 +807,8 @@ static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
row[rows].disable = (1 << i);
rows++;
}
- sort_cliplist(col,cols);
- sort_cliplist(row,rows);
+ sort(col, cols, sizeof col[0], cliplist_cmp, NULL);
+ sort(row, rows, sizeof row[0], cliplist_cmp, NULL);
set_cliplist(dev,0x380,col,cols,"cols");
set_cliplist(dev,0x384,row,rows,"rows");
return 0;
@@ -1261,19 +1260,14 @@ static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh)
static int saa7134_resource(struct saa7134_fh *fh)
{
- int res = 0;
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return RESOURCE_VIDEO;
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- res = RESOURCE_VIDEO;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- res = RESOURCE_VBI;
- break;
- default:
- BUG();
- }
- return res;
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return RESOURCE_VBI;
+
+ BUG();
+ return 0;
}
static int video_open(struct inode *inode, struct file *file)
@@ -1461,8 +1455,7 @@ static int video_release(struct inode *inode, struct file *file)
return 0;
}
-static int
-video_mmap(struct file *file, struct vm_area_struct * vma)
+static int video_mmap(struct file *file, struct vm_area_struct * vma)
{
struct saa7134_fh *fh = file->private_data;
@@ -2461,12 +2454,6 @@ int saa7134_video_init2(struct saa7134_dev *dev)
return 0;
}
-int saa7134_video_fini(struct saa7134_dev *dev)
-{
- /* nothing */
- return 0;
-}
-
void saa7134_irq_video_intl(struct saa7134_dev *dev)
{
static const char *st[] = {
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index b3e3957c89b5..62224cc958f1 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -231,6 +231,10 @@ struct saa7134_format {
#define SAA7134_BOARD_ENCORE_ENLTV 106
#define SAA7134_BOARD_ENCORE_ENLTV_FM 107
#define SAA7134_BOARD_CINERGY_HT_PCI 108
+#define SAA7134_BOARD_PHILIPS_TIGER_S 109
+#define SAA7134_BOARD_AVERMEDIA_M102 110
+#define SAA7134_BOARD_ASUS_P7131_4871 111
+#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112
#define SAA7134_MAXBOARDS 8
#define SAA7134_INPUT_MAX 8
@@ -280,6 +284,7 @@ struct saa7134_board {
unsigned char radio_addr;
unsigned int tda9887_conf;
+ unsigned int tuner_config;
/* peripheral I/O */
enum saa7134_video_out video_out;
@@ -435,6 +440,8 @@ struct saa7134_dev {
#ifdef VIDIOC_G_PRIORITY
struct v4l2_prio_state prio;
#endif
+ /* workstruct for loading modules */
+ struct work_struct request_module_wk;
/* insmod option/autodetected */
int autodetected;
@@ -562,6 +569,8 @@ extern struct list_head saa7134_devlist;
extern int saa7134_no_overlay;
void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value);
+int saa7134_tuner_callback(void *ptr, int command, int arg);
#define SAA7134_PGTABLE_SIZE 4096
@@ -620,7 +629,6 @@ int saa7134_common_ioctl(struct saa7134_dev *dev,
int saa7134_video_init1(struct saa7134_dev *dev);
int saa7134_video_init2(struct saa7134_dev *dev);
-int saa7134_video_fini(struct saa7134_dev *dev);
void saa7134_irq_video_intl(struct saa7134_dev *dev);
void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status);
diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c
index 038448f5a978..93fb04ed99a0 100644
--- a/drivers/media/video/se401.c
+++ b/drivers/media/video/se401.c
@@ -450,6 +450,13 @@ static int se401_start_stream(struct usb_se401 *se401)
}
for (i=0; i<SE401_NUMSBUF; i++) {
se401->sbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
+ if (!se401->sbuf[i].data) {
+ for(i = i - 1; i >= 0; i--) {
+ kfree(se401->sbuf[i].data);
+ se401->sbuf[i].data = NULL;
+ }
+ return -ENOMEM;
+ }
}
se401->bayeroffset=0;
@@ -458,13 +465,26 @@ static int se401_start_stream(struct usb_se401 *se401)
se401->scratch_overflow=0;
for (i=0; i<SE401_NUMSCRATCH; i++) {
se401->scratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
+ if (!se401->scratch[i].data) {
+ for(i = i - 1; i >= 0; i--) {
+ kfree(se401->scratch[i].data);
+ se401->scratch[i].data = NULL;
+ }
+ goto nomem_sbuf;
+ }
se401->scratch[i].state=BUFFER_UNUSED;
}
for (i=0; i<SE401_NUMSBUF; i++) {
urb=usb_alloc_urb(0, GFP_KERNEL);
- if(!urb)
- return -ENOMEM;
+ if(!urb) {
+ for(i = i - 1; i >= 0; i--) {
+ usb_kill_urb(se401->urb[i]);
+ usb_free_urb(se401->urb[i]);
+ se401->urb[i] = NULL;
+ }
+ goto nomem_scratch;
+ }
usb_fill_bulk_urb(urb, se401->dev,
usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT),
@@ -482,6 +502,18 @@ static int se401_start_stream(struct usb_se401 *se401)
se401->framecount=0;
return 0;
+
+ nomem_scratch:
+ for (i=0; i<SE401_NUMSCRATCH; i++) {
+ kfree(se401->scratch[i].data);
+ se401->scratch[i].data = NULL;
+ }
+ nomem_sbuf:
+ for (i=0; i<SE401_NUMSBUF; i++) {
+ kfree(se401->sbuf[i].data);
+ se401->sbuf[i].data = NULL;
+ }
+ return -ENOMEM;
}
static int se401_stop_stream(struct usb_se401 *se401)
diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig
index 1a7ccb666ab0..19204f5686e1 100644
--- a/drivers/media/video/sn9c102/Kconfig
+++ b/drivers/media/video/sn9c102/Kconfig
@@ -1,6 +1,6 @@
config USB_SN9C102
tristate "USB SN9C1xx PC Camera Controller support"
- depends on USB && VIDEO_V4L1
+ depends on USB && VIDEO_V4L2
---help---
Say Y here if you want support for cameras based on SONiX SN9C101,
SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers.
diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile
index 30e3dfe537fe..a56d16f69c71 100644
--- a/drivers/media/video/sn9c102/Makefile
+++ b/drivers/media/video/sn9c102/Makefile
@@ -1,7 +1,14 @@
-sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \
- sn9c102_ov7630.o sn9c102_ov7660.o sn9c102_pas106b.o \
- sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \
- sn9c102_tas5130d1b.o
+sn9c102-objs := sn9c102_core.o \
+ sn9c102_hv7131d.o \
+ sn9c102_hv7131r.o \
+ sn9c102_mi0343.o \
+ sn9c102_mi0360.o \
+ sn9c102_ov7630.o \
+ sn9c102_ov7660.o \
+ sn9c102_pas106b.o \
+ sn9c102_pas202bcb.o \
+ sn9c102_tas5110c1b.o \
+ sn9c102_tas5110d.o \
+ sn9c102_tas5130d1b.o
obj-$(CONFIG_USB_SN9C102) += sn9c102.o
-
diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h
index 5428f34e7c5b..680e74634527 100644
--- a/drivers/media/video/sn9c102/sn9c102.h
+++ b/drivers/media/video/sn9c102/sn9c102.h
@@ -78,8 +78,13 @@ enum sn9c102_stream_state {
typedef char sn9c102_sof_header_t[62];
+struct sn9c102_sof_t {
+ sn9c102_sof_header_t header;
+ u16 bytesread;
+};
+
struct sn9c102_sysfs_attr {
- u8 reg, i2c_reg;
+ u16 reg, i2c_reg;
sn9c102_sof_header_t frame_header;
};
@@ -112,7 +117,7 @@ struct sn9c102_device {
struct v4l2_jpegcompression compression;
struct sn9c102_sysfs_attr sysfs;
- sn9c102_sof_header_t sof_header;
+ struct sn9c102_sof_t sof;
u16 reg[384];
struct sn9c102_module_param module_param;
@@ -182,8 +187,8 @@ do { \
if ((level) == 1 || (level) == 2) \
pr_info("sn9c102: " fmt "\n", ## args); \
else if ((level) == 3) \
- pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \
- __LINE__ , ## args); \
+ pr_debug("sn9c102: [%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args); \
} \
} while (0)
#else
@@ -194,8 +199,8 @@ do { \
#undef PDBG
#define PDBG(fmt, args...) \
-dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
- __FUNCTION__, __LINE__ , ## args)
+dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \
+ __LINE__ , ## args)
#undef PDBGG
#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index d0e2b40a7725..89f83354de3b 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -44,11 +44,12 @@
/*****************************************************************************/
#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers"
-#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia"
+#define SN9C102_MODULE_ALIAS "sn9c1xx"
+#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL"
-#define SN9C102_MODULE_VERSION "1:1.34"
-#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 34)
+#define SN9C102_MODULE_VERSION "1:1.39"
+#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 39)
/*****************************************************************************/
@@ -56,6 +57,7 @@ MODULE_DEVICE_TABLE(usb, sn9c102_id_table);
MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
MODULE_DESCRIPTION(SN9C102_MODULE_NAME);
+MODULE_ALIAS(SN9C102_MODULE_ALIAS);
MODULE_VERSION(SN9C102_MODULE_VERSION);
MODULE_LICENSE(SN9C102_MODULE_LICENSE);
@@ -106,8 +108,7 @@ MODULE_PARM_DESC(debug,
"\n1 = critical errors"
"\n2 = significant informations"
"\n3 = more verbose messages"
- "\nLevel 3 is useful for testing only, when only "
- "one device is used."
+ "\nLevel 3 is useful for testing only."
"\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"."
"\n");
#endif
@@ -121,8 +122,8 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
struct v4l2_pix_format* p = &(cam->sensor.pix_format);
struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
size_t imagesize = cam->module_param.force_munmap || io == IO_READ ?
- (p->width * p->height * p->priv) / 8 :
- (r->width * r->height * p->priv) / 8;
+ (p->width * p->height * p->priv) / 8 :
+ (r->width * r->height * p->priv) / 8;
void* buff = NULL;
u32 i;
@@ -208,27 +209,40 @@ static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
}
/*****************************************************************************/
-
-int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index)
+/*
+ * Write a sequence of count value/register pairs. Returns -1 after the
+ * first failed write, or 0 for no errors.
+ */
+int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2],
+ int count)
{
struct usb_device* udev = cam->usbdev;
+ u8* value = cam->control_buffer; /* Needed for DMA'able memory */
int i, res;
- if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg))
- return -1;
+ for (i = 0; i < count; i++) {
+ u8 index = valreg[i][1];
+
+ /*
+ * index is a u8, so it must be <256 and can't be out of range.
+ * If we put in a check anyway, gcc annoys us with a warning
+ * that our check is useless. People get all uppity when they
+ * see warnings in the kernel compile.
+ */
+
+ *value = valreg[i][0];
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x08, 0x41, index, 0,
+ value, 1, SN9C102_CTRL_TIMEOUT);
+ if (res < 0) {
+ DBG(3, "Failed to write a register (value 0x%02X, "
+ "index 0x%02X, error %d)", *value, index, res);
+ return -1;
+ }
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
- index, 0, buff, sizeof(buff),
- SN9C102_CTRL_TIMEOUT*sizeof(buff));
- if (res < 0) {
- DBG(3, "Failed to write registers (index 0x%02X, error %d)",
- index, res);
- return -1;
+ cam->reg[index] = *value;
}
- for (i = 0; i < sizeof(buff); i++)
- cam->reg[index+i] = buff[i];
-
return 0;
}
@@ -485,18 +499,43 @@ static size_t sn9c102_sof_length(struct sn9c102_device* cam)
static void*
sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
{
- char sof_header[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
- size_t soflen = 0, i;
+ static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
+ const char *m = mem;
+ size_t soflen = 0, i, j;
soflen = sn9c102_sof_length(cam);
- for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
- if (!memcmp(mem + i, sof_header, sizeof(sof_header))) {
- memcpy(cam->sof_header, mem + i,
- sizeof(sn9c102_sof_header_t));
- /* Skip the header */
- return mem + i + soflen;
+ for (i = 0; i < len; i++) {
+ size_t b;
+
+ /* Read the variable part of the header */
+ if (unlikely(cam->sof.bytesread >= sizeof(marker))) {
+ cam->sof.header[cam->sof.bytesread] = *(m+i);
+ if (++cam->sof.bytesread == soflen) {
+ cam->sof.bytesread = 0;
+ return mem + i;
+ }
+ continue;
+ }
+
+ /* Search for the SOF marker (fixed part) in the header */
+ for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) {
+ if (unlikely(i+j) == len)
+ return NULL;
+ if (*(m+i+j) == marker[cam->sof.bytesread]) {
+ cam->sof.header[cam->sof.bytesread] = *(m+i+j);
+ if (++cam->sof.bytesread == sizeof(marker)) {
+ PDBGG("Bytes to analyze: %zd. SOF "
+ "starts at byte #%zd", len, i);
+ i += j+1;
+ break;
+ }
+ } else {
+ cam->sof.bytesread = 0;
+ break;
}
+ }
+ }
return NULL;
}
@@ -505,7 +544,7 @@ sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
static void*
sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
{
- char eof_header[4][4] = {
+ static const u8 eof_header[4][4] = {
{0x00, 0x00, 0x00, 0x00},
{0x40, 0x00, 0x00, 0x00},
{0x80, 0x00, 0x00, 0x00},
@@ -513,10 +552,16 @@ sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
};
size_t i, j;
+ /* The EOF header does not exist in compressed data */
if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X ||
cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
- return NULL; /* EOF header does not exist in compressed data */
+ return NULL;
+ /*
+ The EOF header might cross the packet boundary, but this is not a
+ problem, since the end of a frame is determined by checking its size
+ in the first place.
+ */
for (i = 0; (len >= 4) && (i <= len - 4); i++)
for (j = 0; j < ARRAY_SIZE(eof_header); j++)
if (!memcmp(mem + i, eof_header[j], 4))
@@ -529,7 +574,7 @@ sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
static void
sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f)
{
- static u8 jpeg_header[589] = {
+ static const u8 jpeg_header[589] = {
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06,
0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e,
@@ -639,6 +684,7 @@ static void sn9c102_urb_complete(struct urb *urb)
cam->stream = STREAM_OFF;
if ((*f))
(*f)->state = F_QUEUED;
+ cam->sof.bytesread = 0;
DBG(3, "Stream interrupted by application");
wake_up(&cam->wait_stream);
}
@@ -676,6 +722,7 @@ static void sn9c102_urb_complete(struct urb *urb)
if (status) {
DBG(3, "Error in isochronous frame");
(*f)->state = F_ERROR;
+ cam->sof.bytesread = 0;
continue;
}
@@ -692,13 +739,13 @@ end_of_frame:
if (eof)
img = (eof > pos) ? eof - pos - 1 : 0;
- if ((*f)->buf.bytesused+img > imagesize) {
+ if ((*f)->buf.bytesused + img > imagesize) {
u32 b;
b = (*f)->buf.bytesused + img -
imagesize;
img = imagesize - (*f)->buf.bytesused;
- DBG(3, "Expected EOF not found: "
- "video frame cut");
+ PDBGG("Expected EOF not found: video "
+ "frame cut");
if (eof)
DBG(3, "Exceeded limit: +%u "
"bytes", (unsigned)(b));
@@ -719,11 +766,6 @@ end_of_frame:
V4L2_PIX_FMT_JPEG) && eof)) {
u32 b;
- if (cam->sensor.pix_format.pixelformat
- == V4L2_PIX_FMT_JPEG)
- sn9c102_write_eoimarker(cam,
- (*f));
-
b = (*f)->buf.bytesused;
(*f)->state = F_DONE;
(*f)->buf.sequence= ++cam->frame_count;
@@ -741,7 +783,7 @@ end_of_frame:
spin_unlock(&cam->queue_lock);
memcpy(cam->sysfs.frame_header,
- cam->sof_header, soflen);
+ cam->sof.header, soflen);
DBG(3, "Video frame captured: %lu "
"bytes", (unsigned long)(b));
@@ -791,7 +833,13 @@ start_of_frame:
V4L2_PIX_FMT_SN9C10X ||
cam->sensor.pix_format.pixelformat ==
V4L2_PIX_FMT_JPEG) {
- eof = sof - soflen;
+ if (sof - pos >= soflen) {
+ eof = sof - soflen;
+ } else { /* remove header */
+ eof = pos;
+ (*f)->buf.bytesused -=
+ (soflen - (sof - pos));
+ }
goto end_of_frame;
} else {
DBG(3, "SOF before expected EOF after "
@@ -878,6 +926,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
}
cam->frame_current = NULL;
+ cam->sof.bytesread = 0;
for (i = 0; i < SN9C102_URBS; i++) {
err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
@@ -959,9 +1008,9 @@ static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count)
if (len < 6) {
strncpy(str, buff, len);
- str[len+1] = '\0';
+ str[len] = '\0';
} else {
- strncpy(str, buff, 4);
+ strncpy(str, buff, 6);
str[6] = '\0';
}
@@ -1062,7 +1111,7 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf)
count = sprintf(buf, "%d\n", val);
- DBG(3, "Read bytes: %zd", count);
+ DBG(3, "Read bytes: %zd, value: %d", count, val);
mutex_unlock(&sn9c102_sysfs_lock);
@@ -1197,7 +1246,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
count = sprintf(buf, "%d\n", val);
- DBG(3, "Read bytes: %zd", count);
+ DBG(3, "Read bytes: %zd, value: %d", count, val);
mutex_unlock(&sn9c102_sysfs_lock);
@@ -1371,35 +1420,35 @@ static CLASS_DEVICE_ATTR(frame_header, S_IRUGO,
static int sn9c102_create_sysfs(struct sn9c102_device* cam)
{
- struct video_device *v4ldev = cam->v4ldev;
+ struct class_device *classdev = &(cam->v4ldev->class_dev);
int err = 0;
- if ((err = video_device_create_file(v4ldev, &class_device_attr_reg)))
+ if ((err = class_device_create_file(classdev, &class_device_attr_reg)))
goto err_out;
- if ((err = video_device_create_file(v4ldev, &class_device_attr_val)))
+ if ((err = class_device_create_file(classdev, &class_device_attr_val)))
goto err_reg;
- if ((err = video_device_create_file(v4ldev,
+ if ((err = class_device_create_file(classdev,
&class_device_attr_frame_header)))
goto err_val;
if (cam->sensor.sysfs_ops) {
- if ((err = video_device_create_file(v4ldev,
+ if ((err = class_device_create_file(classdev,
&class_device_attr_i2c_reg)))
goto err_frame_header;
- if ((err = video_device_create_file(v4ldev,
+ if ((err = class_device_create_file(classdev,
&class_device_attr_i2c_val)))
goto err_i2c_reg;
}
if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) {
- if ((err = video_device_create_file(v4ldev,
+ if ((err = class_device_create_file(classdev,
&class_device_attr_green)))
goto err_i2c_val;
} else {
- if ((err = video_device_create_file(v4ldev,
+ if ((err = class_device_create_file(classdev,
&class_device_attr_blue)))
goto err_i2c_val;
- if ((err = video_device_create_file(v4ldev,
+ if ((err = class_device_create_file(classdev,
&class_device_attr_red)))
goto err_blue;
}
@@ -1407,19 +1456,19 @@ static int sn9c102_create_sysfs(struct sn9c102_device* cam)
return 0;
err_blue:
- video_device_remove_file(v4ldev, &class_device_attr_blue);
+ class_device_remove_file(classdev, &class_device_attr_blue);
err_i2c_val:
if (cam->sensor.sysfs_ops)
- video_device_remove_file(v4ldev, &class_device_attr_i2c_val);
+ class_device_remove_file(classdev, &class_device_attr_i2c_val);
err_i2c_reg:
if (cam->sensor.sysfs_ops)
- video_device_remove_file(v4ldev, &class_device_attr_i2c_reg);
+ class_device_remove_file(classdev, &class_device_attr_i2c_reg);
err_frame_header:
- video_device_remove_file(v4ldev, &class_device_attr_frame_header);
+ class_device_remove_file(classdev, &class_device_attr_frame_header);
err_val:
- video_device_remove_file(v4ldev, &class_device_attr_val);
+ class_device_remove_file(classdev, &class_device_attr_val);
err_reg:
- video_device_remove_file(v4ldev, &class_device_attr_reg);
+ class_device_remove_file(classdev, &class_device_attr_reg);
err_out:
return err;
}
@@ -1477,10 +1526,10 @@ sn9c102_set_compression(struct sn9c102_device* cam,
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
case BRIDGE_SN9C103:
- if (compression->quality == 0)
+ if (compression->quality == 0)
err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01,
0x17);
- else if (compression->quality == 1)
+ else if (compression->quality == 1)
err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe,
0x17);
break;
@@ -1489,10 +1538,10 @@ sn9c102_set_compression(struct sn9c102_device* cam,
if (compression->quality == 0) {
for (i = 0; i <= 63; i++) {
err += sn9c102_write_reg(cam,
- SN9C102_Y_QTABLE0[i],
+ SN9C102_Y_QTABLE1[i],
0x100 + i);
err += sn9c102_write_reg(cam,
- SN9C102_UV_QTABLE0[i],
+ SN9C102_UV_QTABLE1[i],
0x140 + i);
}
err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf,
@@ -1597,9 +1646,13 @@ static int sn9c102_init(struct sn9c102_device* cam)
if (cam->bridge == BRIDGE_SN9C101 ||
cam->bridge == BRIDGE_SN9C102 ||
cam->bridge == BRIDGE_SN9C103) {
+ if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
+ s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8;
cam->compression.quality = cam->reg[0x17] & 0x01 ?
0 : 1;
} else {
+ if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+ s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG;
cam->compression.quality = cam->reg[0x18] & 0x40 ?
0 : 1;
err += sn9c102_set_compression(cam, &cam->compression);
@@ -1805,7 +1858,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
DBG(3, "Close and open the device again to choose "
"the read method");
mutex_unlock(&cam->fileop_mutex);
- return -EINVAL;
+ return -EBUSY;
}
if (cam->io == IO_NONE) {
@@ -1845,16 +1898,16 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
return err;
}
} else {
- timeout = wait_event_interruptible_timeout
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED),
- cam->module_param.frame_timeout *
- 1000 * msecs_to_jiffies(1) );
- if (timeout < 0) {
- mutex_unlock(&cam->fileop_mutex);
- return timeout;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
} else if (timeout == 0 &&
!(cam->state & DEV_DISCONNECTED)) {
DBG(1, "Video frame timeout elapsed");
@@ -2001,7 +2054,12 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
return -EIO;
}
- if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+ if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EACCES;
+ }
+
+ if (cam->io != IO_MMAP ||
size != PAGE_ALIGN(cam->frame[0].buf.length)) {
mutex_unlock(&cam->fileop_mutex);
return -EINVAL;
@@ -2267,7 +2325,7 @@ sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_CROP failed. "
"Unmap the buffers first.");
- return -EINVAL;
+ return -EBUSY;
}
/* Preserve R,G or B origin */
@@ -2410,8 +2468,8 @@ sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg)
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
case BRIDGE_SN9C103:
- strcpy(fmtd.description, "compressed");
- fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
+ strcpy(fmtd.description, "compressed");
+ fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
break;
case BRIDGE_SN9C105:
case BRIDGE_SN9C120:
@@ -2445,8 +2503,10 @@ sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X ||
- pfmt->pixelformat==V4L2_PIX_FMT_JPEG)
+ pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ?
+ V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB;
+ pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X ||
+ pfmt->pixelformat == V4L2_PIX_FMT_JPEG)
? 0 : (pfmt->width * pfmt->priv) / 8;
pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
pfmt->field = V4L2_FIELD_NONE;
@@ -2521,9 +2581,9 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
case BRIDGE_SN9C103:
- if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
- pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
- pix->pixelformat = pfmt->pixelformat;
+ if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
+ pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+ pix->pixelformat = pfmt->pixelformat;
break;
case BRIDGE_SN9C105:
case BRIDGE_SN9C120:
@@ -2533,7 +2593,8 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
break;
}
pix->priv = pfmt->priv; /* bpp */
- pix->colorspace = pfmt->colorspace;
+ pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ?
+ V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB;
pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X ||
pix->pixelformat == V4L2_PIX_FMT_JPEG)
? 0 : (pix->width * pix->priv) / 8;
@@ -2551,7 +2612,7 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_FMT failed. Unmap the "
"buffers first.");
- return -EINVAL;
+ return -EBUSY;
}
if (cam->stream == STREAM_ON)
@@ -2666,14 +2727,14 @@ sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg)
if (cam->io == IO_READ) {
DBG(3, "Close and open the device again to choose the mmap "
"I/O method");
- return -EINVAL;
+ return -EBUSY;
}
for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are "
"still mapped.");
- return -EINVAL;
+ return -EBUSY;
}
if (cam->stream == STREAM_ON)
@@ -2785,15 +2846,15 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
if (err)
return err;
} else {
- timeout = wait_event_interruptible_timeout
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED),
- cam->module_param.frame_timeout *
- 1000 * msecs_to_jiffies(1) );
- if (timeout < 0)
- return timeout;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
else if (timeout == 0 &&
!(cam->state & DEV_DISCONNECTED)) {
DBG(1, "Video frame timeout elapsed");
@@ -2837,9 +2898,6 @@ sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg)
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL;
- if (list_empty(&cam->inqueue))
- return -EINVAL;
-
cam->stream = STREAM_ON;
DBG(3, "Stream on");
@@ -3166,8 +3224,8 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
r = sn9c102_read_reg(cam, 0x00);
if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) {
- DBG(1, "Sorry, this is not a SN9C1xx based camera "
- "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ DBG(1, "Sorry, this is not a SN9C1xx-based camera "
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
err = -ENODEV;
goto fail;
}
@@ -3177,19 +3235,19 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
DBG(2, "SN9C10[12] PC Camera Controller detected "
- "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
break;
case BRIDGE_SN9C103:
DBG(2, "SN9C103 PC Camera Controller detected "
- "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
break;
case BRIDGE_SN9C105:
DBG(2, "SN9C105 PC Camera Controller detected "
- "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
break;
case BRIDGE_SN9C120:
DBG(2, "SN9C120 PC Camera Controller detected "
- "(vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
break;
}
@@ -3260,6 +3318,8 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
"device controlling. Error #%d", err);
#else
DBG(2, "Optional device control through 'sysfs' interface disabled");
+ DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' "
+ "configuration option to enable it.");
#endif
usb_set_intfdata(intf, cam);
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
index 3a682eca6c65..f49bd8c5b86e 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -89,16 +89,22 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), },
/* SN9C120 */
+ { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), },
@@ -114,12 +120,15 @@ static const struct usb_device_id sn9c102_id_table[] = {
Functions must return 0 on success, the appropriate error otherwise.
*/
extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam);
+extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam);
extern int sn9c102_probe_mi0343(struct sn9c102_device* cam);
+extern int sn9c102_probe_mi0360(struct sn9c102_device* cam);
extern int sn9c102_probe_ov7630(struct sn9c102_device* cam);
extern int sn9c102_probe_ov7660(struct sn9c102_device* cam);
extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam);
extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam);
extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
/*
@@ -128,13 +137,16 @@ extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
the order of the list below, from top to bottom.
*/
static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = {
+ &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */
&sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */
&sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */
&sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */
- &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */
&sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */
&sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */
&sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */
+ &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */
&sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */
NULL,
};
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
index 7ae368f60d89..28a861aed044 100644
--- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
@@ -22,19 +22,13 @@
#include "sn9c102_sensor.h"
-static struct sn9c102_sensor hv7131d;
-
-
static int hv7131d_init(struct sn9c102_device* cam)
{
- int err = 0;
+ int err;
- err += sn9c102_write_reg(cam, 0x00, 0x10);
- err += sn9c102_write_reg(cam, 0x00, 0x11);
- err += sn9c102_write_reg(cam, 0x00, 0x14);
- err += sn9c102_write_reg(cam, 0x60, 0x17);
- err += sn9c102_write_reg(cam, 0x0e, 0x18);
- err += sn9c102_write_reg(cam, 0xf2, 0x19);
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x00, 0x14}, {0x60, 0x17},
+ {0x0e, 0x18}, {0xf2, 0x19});
err += sn9c102_i2c_write(cam, 0x01, 0x04);
err += sn9c102_i2c_write(cam, 0x02, 0x00);
@@ -153,7 +147,7 @@ static int hv7131d_set_pix_format(struct sn9c102_device* cam,
static struct sn9c102_sensor hv7131d = {
.name = "HV7131D",
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
- .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
.sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_2WIRES,
@@ -250,11 +244,10 @@ static struct sn9c102_sensor hv7131d = {
int sn9c102_probe_hv7131d(struct sn9c102_device* cam)
{
- int r0 = 0, r1 = 0, err = 0;
+ int r0 = 0, r1 = 0, err;
- err += sn9c102_write_reg(cam, 0x01, 0x01);
- err += sn9c102_write_reg(cam, 0x00, 0x01);
- err += sn9c102_write_reg(cam, 0x28, 0x17);
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17});
if (err)
return -EIO;
@@ -263,7 +256,7 @@ int sn9c102_probe_hv7131d(struct sn9c102_device* cam)
if (r0 < 0 || r1 < 0)
return -EIO;
- if (r0 != 0x00 && r1 != 0x04)
+ if (r0 != 0x00 || r1 != 0x04)
return -ENODEV;
sn9c102_attach_sensor(cam, &hv7131d);
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/video/sn9c102/sn9c102_hv7131r.c
new file mode 100644
index 000000000000..5a495baa5f95
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131r.c
@@ -0,0 +1,366 @@
+/***************************************************************************
+ * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * 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 "sn9c102_sensor.h"
+
+
+static int hv7131r_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C103:
+ err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04},
+ {0x20, 0x05}, {0x20, 0x06},
+ {0x03, 0x10}, {0x00, 0x14},
+ {0x60, 0x17}, {0x0a, 0x18},
+ {0xf0, 0x19}, {0x1d, 0x1a},
+ {0x10, 0x1b}, {0x02, 0x1c},
+ {0x03, 0x1d}, {0x0f, 0x1e},
+ {0x0c, 0x1f}, {0x00, 0x20},
+ {0x10, 0x21}, {0x20, 0x22},
+ {0x30, 0x23}, {0x40, 0x24},
+ {0x50, 0x25}, {0x60, 0x26},
+ {0x70, 0x27}, {0x80, 0x28},
+ {0x90, 0x29}, {0xa0, 0x2a},
+ {0xb0, 0x2b}, {0xc0, 0x2c},
+ {0xd0, 0x2d}, {0xe0, 0x2e},
+ {0xf0, 0x2f}, {0xff, 0x30});
+
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02},
+ {0x00, 0x03}, {0x1a, 0x04},
+ {0x44, 0x05}, {0x3e, 0x06},
+ {0x1a, 0x07}, {0x03, 0x10},
+ {0x08, 0x14}, {0xa3, 0x17},
+ {0x4b, 0x18}, {0x00, 0x19},
+ {0x1d, 0x1a}, {0x10, 0x1b},
+ {0x02, 0x1c}, {0x03, 0x1d},
+ {0x0f, 0x1e}, {0x0c, 0x1f},
+ {0x00, 0x20}, {0x29, 0x21},
+ {0x40, 0x22}, {0x54, 0x23},
+ {0x66, 0x24}, {0x76, 0x25},
+ {0x85, 0x26}, {0x94, 0x27},
+ {0xa1, 0x28}, {0xae, 0x29},
+ {0xbb, 0x2a}, {0xc7, 0x2b},
+ {0xd3, 0x2c}, {0xde, 0x2d},
+ {0xea, 0x2e}, {0xf4, 0x2f},
+ {0xff, 0x30}, {0x00, 0x3F},
+ {0xC7, 0x40}, {0x01, 0x41},
+ {0x44, 0x42}, {0x00, 0x43},
+ {0x44, 0x44}, {0x00, 0x45},
+ {0x44, 0x46}, {0x00, 0x47},
+ {0xC7, 0x48}, {0x01, 0x49},
+ {0xC7, 0x4A}, {0x01, 0x4B},
+ {0xC7, 0x4C}, {0x01, 0x4D},
+ {0x44, 0x4E}, {0x00, 0x4F},
+ {0x44, 0x50}, {0x00, 0x51},
+ {0x44, 0x52}, {0x00, 0x53},
+ {0xC7, 0x54}, {0x01, 0x55},
+ {0xC7, 0x56}, {0x01, 0x57},
+ {0xC7, 0x58}, {0x01, 0x59},
+ {0x44, 0x5A}, {0x00, 0x5B},
+ {0x44, 0x5C}, {0x00, 0x5D},
+ {0x44, 0x5E}, {0x00, 0x5F},
+ {0xC7, 0x60}, {0x01, 0x61},
+ {0xC7, 0x62}, {0x01, 0x63},
+ {0xC7, 0x64}, {0x01, 0x65},
+ {0x44, 0x66}, {0x00, 0x67},
+ {0x44, 0x68}, {0x00, 0x69},
+ {0x44, 0x6A}, {0x00, 0x6B},
+ {0xC7, 0x6C}, {0x01, 0x6D},
+ {0xC7, 0x6E}, {0x01, 0x6F},
+ {0xC7, 0x70}, {0x01, 0x71},
+ {0x44, 0x72}, {0x00, 0x73},
+ {0x44, 0x74}, {0x00, 0x75},
+ {0x44, 0x76}, {0x00, 0x77},
+ {0xC7, 0x78}, {0x01, 0x79},
+ {0xC7, 0x7A}, {0x01, 0x7B},
+ {0xC7, 0x7C}, {0x01, 0x7D},
+ {0x44, 0x7E}, {0x00, 0x7F},
+ {0x14, 0x84}, {0x00, 0x85},
+ {0x27, 0x86}, {0x00, 0x87},
+ {0x07, 0x88}, {0x00, 0x89},
+ {0xEC, 0x8A}, {0x0f, 0x8B},
+ {0xD8, 0x8C}, {0x0f, 0x8D},
+ {0x3D, 0x8E}, {0x00, 0x8F},
+ {0x3D, 0x90}, {0x00, 0x91},
+ {0xCD, 0x92}, {0x0f, 0x93},
+ {0xf7, 0x94}, {0x0f, 0x95},
+ {0x0C, 0x96}, {0x00, 0x97},
+ {0x00, 0x98}, {0x66, 0x99},
+ {0x05, 0x9A}, {0x00, 0x9B},
+ {0x04, 0x9C}, {0x00, 0x9D},
+ {0x08, 0x9E}, {0x00, 0x9F},
+ {0x2D, 0xC0}, {0x2D, 0xC1},
+ {0x3A, 0xC2}, {0x05, 0xC3},
+ {0x04, 0xC4}, {0x3F, 0xC5},
+ {0x00, 0xC6}, {0x00, 0xC7},
+ {0x50, 0xC8}, {0x3C, 0xC9},
+ {0x28, 0xCA}, {0xD8, 0xCB},
+ {0x14, 0xCC}, {0xEC, 0xCD},
+ {0x32, 0xCE}, {0xDD, 0xCF},
+ {0x32, 0xD0}, {0xDD, 0xD1},
+ {0x6A, 0xD2}, {0x50, 0xD3},
+ {0x00, 0xD4}, {0x00, 0xD5},
+ {0x00, 0xD6});
+ break;
+ default:
+ break;
+ }
+
+ err += sn9c102_i2c_write(cam, 0x20, 0x00);
+ err += sn9c102_i2c_write(cam, 0x21, 0xd6);
+ err += sn9c102_i2c_write(cam, 0x25, 0x06);
+
+ return err;
+}
+
+
+static int hv7131r_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0)
+ return -EIO;
+ ctrl->value = ctrl->value & 0x3f;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0)
+ return -EIO;
+ ctrl->value = ctrl->value & 0x3f;
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0)
+ return -EIO;
+ ctrl->value = ctrl->value & 0x3f;
+ return 0;
+ case V4L2_CID_BLACK_LEVEL:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0)
+ return -EIO;
+ ctrl->value = (ctrl->value & 0x08) ? 1 : 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int hv7131r_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x30, ctrl->value);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x31, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x33, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x32, ctrl->value);
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ {
+ int r = sn9c102_i2c_read(cam, 0x01);
+ if (r < 0)
+ return -EIO;
+ err += sn9c102_i2c_write(cam, 0x01,
+ (ctrl->value<<3) | (r&0xf7));
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int hv7131r_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int hv7131r_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C103:
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+ err += sn9c102_write_reg(cam, 0xa0, 0x19);
+ err += sn9c102_i2c_write(cam, 0x01, 0x04);
+ } else {
+ err += sn9c102_write_reg(cam, 0x30, 0x19);
+ err += sn9c102_i2c_write(cam, 0x01, 0x04);
+ }
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+ err += sn9c102_write_reg(cam, 0xa5, 0x17);
+ err += sn9c102_i2c_write(cam, 0x01, 0x24);
+ } else {
+ err += sn9c102_write_reg(cam, 0xa3, 0x17);
+ err += sn9c102_i2c_write(cam, 0x01, 0x04);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
+
+static struct sn9c102_sensor hv7131r = {
+ .name = "HV7131R",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120,
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x11,
+ .init = &hv7131r_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x40,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x08,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x1a,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x2f,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLACK_LEVEL,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto black level compensation",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &hv7131r_get_ctrl,
+ .set_ctrl = &hv7131r_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &hv7131r_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &hv7131r_set_pix_format
+};
+
+
+int sn9c102_probe_hv7131r(struct sn9c102_device* cam)
+{
+ int devid, err;
+
+ err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02},
+ {0x34, 0x01}, {0x20, 0x17},
+ {0x34, 0x01}, {0x46, 0x01});
+
+ if (err)
+ return -EIO;
+
+ devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00);
+ if (devid < 0)
+ return -EIO;
+
+ if (devid != 0x02)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &hv7131r);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c
index a33d1bc10f90..9200845d011b 100644
--- a/drivers/media/video/sn9c102/sn9c102_mi0343.c
+++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c
@@ -22,36 +22,30 @@
#include "sn9c102_sensor.h"
-static struct sn9c102_sensor mi0343;
-static u8 mi0343_i2c_data[5+1];
-
-
static int mi0343_init(struct sn9c102_device* cam)
{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
int err = 0;
- err += sn9c102_write_reg(cam, 0x00, 0x10);
- err += sn9c102_write_reg(cam, 0x00, 0x11);
- err += sn9c102_write_reg(cam, 0x0a, 0x14);
- err += sn9c102_write_reg(cam, 0x40, 0x01);
- err += sn9c102_write_reg(cam, 0x20, 0x17);
- err += sn9c102_write_reg(cam, 0x07, 0x18);
- err += sn9c102_write_reg(cam, 0xa0, 0x19);
-
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
- 0x0d, 0x00, 0x01, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
- 0x0d, 0x00, 0x00, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
- 0x03, 0x01, 0xe1, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
- 0x04, 0x02, 0x81, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
- 0x05, 0x00, 0x17, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
- 0x06, 0x00, 0x11, 0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
- 0x62, 0x04, 0x9a, 0, 0);
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x0a, 0x14}, {0x40, 0x01},
+ {0x20, 0x17}, {0x07, 0x18},
+ {0xa0, 0x19});
+
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
+ 0x01, 0xe1, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
+ 0x02, 0x81, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
+ 0x00, 0x17, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
+ 0x00, 0x11, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
+ 0x04, 0x9a, 0, 0);
return err;
}
@@ -60,43 +54,46 @@ static int mi0343_init(struct sn9c102_device* cam)
static int mi0343_get_ctrl(struct sn9c102_device* cam,
struct v4l2_control* ctrl)
{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ u8 data[5+1];
+
switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
- if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
- 0x09, 2+1, mi0343_i2c_data) < 0)
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09,
+ 2+1, data) < 0)
return -EIO;
- ctrl->value = mi0343_i2c_data[2];
+ ctrl->value = data[2];
return 0;
case V4L2_CID_GAIN:
- if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
- 0x35, 2+1, mi0343_i2c_data) < 0)
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35,
+ 2+1, data) < 0)
return -EIO;
break;
case V4L2_CID_HFLIP:
- if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
- 0x20, 2+1, mi0343_i2c_data) < 0)
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
+ 2+1, data) < 0)
return -EIO;
- ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0;
+ ctrl->value = data[3] & 0x20 ? 1 : 0;
return 0;
case V4L2_CID_VFLIP:
- if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
- 0x20, 2+1, mi0343_i2c_data) < 0)
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
+ 2+1, data) < 0)
return -EIO;
- ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0;
+ ctrl->value = data[3] & 0x80 ? 1 : 0;
return 0;
case V4L2_CID_RED_BALANCE:
- if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
- 0x2d, 2+1, mi0343_i2c_data) < 0)
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d,
+ 2+1, data) < 0)
return -EIO;
break;
case V4L2_CID_BLUE_BALANCE:
- if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
- 0x2c, 2+1, mi0343_i2c_data) < 0)
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c,
+ 2+1, data) < 0)
return -EIO;
break;
case SN9C102_V4L2_CID_GREEN_BALANCE:
- if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
- 0x2e, 2+1, mi0343_i2c_data) < 0)
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e,
+ 2+1, data) < 0)
return -EIO;
break;
default:
@@ -108,7 +105,7 @@ static int mi0343_get_ctrl(struct sn9c102_device* cam,
case V4L2_CID_RED_BALANCE:
case V4L2_CID_BLUE_BALANCE:
case SN9C102_V4L2_CID_GREEN_BALANCE:
- ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8);
+ ctrl->value = data[3] | (data[2] << 8);
if (ctrl->value >= 0x10 && ctrl->value <= 0x3f)
ctrl->value -= 0x10;
else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f)
@@ -124,6 +121,7 @@ static int mi0343_get_ctrl(struct sn9c102_device* cam,
static int mi0343_set_ctrl(struct sn9c102_device* cam,
const struct v4l2_control* ctrl)
{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
u16 reg = 0;
int err = 0;
@@ -143,50 +141,42 @@ static int mi0343_set_ctrl(struct sn9c102_device* cam,
switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x09, ctrl->value, 0x00,
0, 0);
break;
case V4L2_CID_GAIN:
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x35, reg >> 8, reg & 0xff,
0, 0);
break;
case V4L2_CID_HFLIP:
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x20, ctrl->value ? 0x40:0x00,
ctrl->value ? 0x20:0x00,
0, 0);
break;
case V4L2_CID_VFLIP:
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x20, ctrl->value ? 0x80:0x00,
ctrl->value ? 0x80:0x00,
0, 0);
break;
case V4L2_CID_RED_BALANCE:
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x2d, reg >> 8, reg & 0xff,
0, 0);
break;
case V4L2_CID_BLUE_BALANCE:
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x2c, reg >> 8, reg & 0xff,
0, 0);
break;
case SN9C102_V4L2_CID_GREEN_BALANCE:
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x2b, reg >> 8, reg & 0xff,
0, 0);
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x2e, reg >> 8, reg & 0xff,
0, 0);
break;
@@ -216,16 +206,15 @@ static int mi0343_set_crop(struct sn9c102_device* cam,
static int mi0343_set_pix_format(struct sn9c102_device* cam,
const struct v4l2_pix_format* pix)
{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
int err = 0;
if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x0a, 0x00, 0x03, 0, 0);
err += sn9c102_write_reg(cam, 0x20, 0x19);
} else {
- err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
- mi0343.i2c_slave_id,
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
0x0a, 0x00, 0x05, 0, 0);
err += sn9c102_write_reg(cam, 0xa0, 0x19);
}
@@ -237,7 +226,7 @@ static int mi0343_set_pix_format(struct sn9c102_device* cam,
static struct sn9c102_sensor mi0343 = {
.name = "MI-0343",
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
- .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_2WIRES,
.i2c_slave_id = 0x5d,
@@ -343,19 +332,20 @@ static struct sn9c102_sensor mi0343 = {
int sn9c102_probe_mi0343(struct sn9c102_device* cam)
{
+ u8 data[5+1];
int err = 0;
- err += sn9c102_write_reg(cam, 0x01, 0x01);
- err += sn9c102_write_reg(cam, 0x00, 0x01);
- err += sn9c102_write_reg(cam, 0x28, 0x17);
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17});
+
if (err)
return -EIO;
if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00,
- 2, mi0343_i2c_data) < 0)
+ 2, data) < 0)
return -EIO;
- if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3)
+ if (data[4] != 0x32 || data[3] != 0xe3)
return -ENODEV;
sn9c102_attach_sensor(cam, &mi0343);
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c
new file mode 100644
index 000000000000..64698acb0b15
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_mi0360.c
@@ -0,0 +1,338 @@
+/***************************************************************************
+ * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * 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 "sn9c102_sensor.h"
+
+
+static int mi0360_init(struct sn9c102_device* cam)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x0a, 0x14}, {0x40, 0x01},
+ {0x20, 0x17}, {0x07, 0x18},
+ {0xa0, 0x19}, {0x02, 0x1c},
+ {0x03, 0x1d}, {0x0f, 0x1e},
+ {0x0c, 0x1f}, {0x00, 0x20},
+ {0x10, 0x21}, {0x20, 0x22},
+ {0x30, 0x23}, {0x40, 0x24},
+ {0x50, 0x25}, {0x60, 0x26},
+ {0x70, 0x27}, {0x80, 0x28},
+ {0x90, 0x29}, {0xa0, 0x2a},
+ {0xb0, 0x2b}, {0xc0, 0x2c},
+ {0xd0, 0x2d}, {0xe0, 0x2e},
+ {0xf0, 0x2f}, {0xff, 0x30});
+
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
+ 0x01, 0xe1, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
+ 0x02, 0x81, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
+ 0x00, 0x17, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
+ 0x00, 0x11, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
+ 0x04, 0x9a, 0, 0);
+
+ return err;
+}
+
+
+static int mi0360_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ u8 data[5+1];
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09,
+ 2+1, data) < 0)
+ return -EIO;
+ ctrl->value = data[2];
+ return 0;
+ case V4L2_CID_GAIN:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35,
+ 2+1, data) < 0)
+ return -EIO;
+ ctrl->value = data[3];
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c,
+ 2+1, data) < 0)
+ return -EIO;
+ ctrl->value = data[3];
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d,
+ 2+1, data) < 0)
+ return -EIO;
+ ctrl->value = data[3];
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e,
+ 2+1, data) < 0)
+ return -EIO;
+ ctrl->value = data[3];
+ return 0;
+ case V4L2_CID_HFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
+ 2+1, data) < 0)
+ return -EIO;
+ ctrl->value = data[3] & 0x20 ? 1 : 0;
+ return 0;
+ case V4L2_CID_VFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20,
+ 2+1, data) < 0)
+ return -EIO;
+ ctrl->value = data[3] & 0x80 ? 1 : 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int mi0360_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x09, ctrl->value, 0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x35, 0x03, ctrl->value,
+ 0, 0);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2c, 0x03, ctrl->value,
+ 0, 0);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2d, 0x03, ctrl->value,
+ 0, 0);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2b, 0x03, ctrl->value,
+ 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2e, 0x03, ctrl->value,
+ 0, 0);
+ break;
+ case V4L2_CID_HFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x20, ctrl->value ? 0x40:0x00,
+ ctrl->value ? 0x20:0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_VFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x20, ctrl->value ? 0x80:0x00,
+ ctrl->value ? 0x80:0x00,
+ 0, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int mi0360_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int mi0360_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x0a, 0x00, 0x02, 0, 0);
+ err += sn9c102_write_reg(cam, 0x20, 0x19);
+ } else {
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x0a, 0x00, 0x05, 0, 0);
+ err += sn9c102_write_reg(cam, 0x60, 0x19);
+ }
+
+ return err;
+}
+
+
+static struct sn9c102_sensor mi0360 = {
+ .name = "MI-0360",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C103,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x5d,
+ .init = &mi0360_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x05,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x25,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x0f,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x32,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x25,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &mi0360_get_ctrl,
+ .set_ctrl = &mi0360_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &mi0360_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &mi0360_set_pix_format
+};
+
+
+int sn9c102_probe_mi0360(struct sn9c102_device* cam)
+{
+ u8 data[5+1];
+ int err;
+
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17});
+ if (err)
+ return -EIO;
+
+ if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00,
+ 2+1, data) < 0)
+ return -EIO;
+
+ if (data[2] != 0x82 || data[3] != 0x43)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &mi0360);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c
index 7df09ff38e63..31b6080b0615 100644
--- a/drivers/media/video/sn9c102/sn9c102_ov7630.c
+++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c
@@ -22,9 +22,6 @@
#include "sn9c102_sensor.h"
-static struct sn9c102_sensor ov7630;
-
-
static int ov7630_init(struct sn9c102_device* cam)
{
int err = 0;
@@ -32,21 +29,20 @@ static int ov7630_init(struct sn9c102_device* cam)
switch (sn9c102_get_bridge(cam)) {
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
- err += sn9c102_write_reg(cam, 0x00, 0x14);
- err += sn9c102_write_reg(cam, 0x60, 0x17);
- err += sn9c102_write_reg(cam, 0x0f, 0x18);
- err += sn9c102_write_reg(cam, 0x50, 0x19);
+ err = sn9c102_write_const_regs(cam, {0x00, 0x14},
+ {0x60, 0x17}, {0x0f, 0x18},
+ {0x50, 0x19});
err += sn9c102_i2c_write(cam, 0x12, 0x8d);
err += sn9c102_i2c_write(cam, 0x12, 0x0d);
err += sn9c102_i2c_write(cam, 0x11, 0x00);
- err += sn9c102_i2c_write(cam, 0x15, 0x34);
- err += sn9c102_i2c_write(cam, 0x16, 0x03);
- err += sn9c102_i2c_write(cam, 0x17, 0x1c);
- err += sn9c102_i2c_write(cam, 0x18, 0xbd);
- err += sn9c102_i2c_write(cam, 0x19, 0x06);
- err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
- err += sn9c102_i2c_write(cam, 0x1b, 0x04);
+ err += sn9c102_i2c_write(cam, 0x15, 0x35);
+ err += sn9c102_i2c_write(cam, 0x16, 0x03);
+ err += sn9c102_i2c_write(cam, 0x17, 0x1c);
+ err += sn9c102_i2c_write(cam, 0x18, 0xbd);
+ err += sn9c102_i2c_write(cam, 0x19, 0x06);
+ err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
+ err += sn9c102_i2c_write(cam, 0x1b, 0x04);
err += sn9c102_i2c_write(cam, 0x20, 0x44);
err += sn9c102_i2c_write(cam, 0x23, 0xee);
err += sn9c102_i2c_write(cam, 0x26, 0xa0);
@@ -65,42 +61,26 @@ static int ov7630_init(struct sn9c102_device* cam)
err += sn9c102_i2c_write(cam, 0x71, 0x00);
err += sn9c102_i2c_write(cam, 0x74, 0x21);
err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
+
break;
case BRIDGE_SN9C103:
- err += sn9c102_write_reg(cam, 0x00, 0x02);
- err += sn9c102_write_reg(cam, 0x00, 0x03);
- err += sn9c102_write_reg(cam, 0x1a, 0x04);
- err += sn9c102_write_reg(cam, 0x20, 0x05);
- err += sn9c102_write_reg(cam, 0x20, 0x06);
- err += sn9c102_write_reg(cam, 0x20, 0x07);
- err += sn9c102_write_reg(cam, 0x03, 0x10);
- err += sn9c102_write_reg(cam, 0x0a, 0x14);
- err += sn9c102_write_reg(cam, 0x60, 0x17);
- err += sn9c102_write_reg(cam, 0x0f, 0x18);
- err += sn9c102_write_reg(cam, 0x50, 0x19);
- err += sn9c102_write_reg(cam, 0x1d, 0x1a);
- err += sn9c102_write_reg(cam, 0x10, 0x1b);
- err += sn9c102_write_reg(cam, 0x02, 0x1c);
- err += sn9c102_write_reg(cam, 0x03, 0x1d);
- err += sn9c102_write_reg(cam, 0x0f, 0x1e);
- err += sn9c102_write_reg(cam, 0x0c, 0x1f);
- err += sn9c102_write_reg(cam, 0x00, 0x20);
- err += sn9c102_write_reg(cam, 0x10, 0x21);
- err += sn9c102_write_reg(cam, 0x20, 0x22);
- err += sn9c102_write_reg(cam, 0x30, 0x23);
- err += sn9c102_write_reg(cam, 0x40, 0x24);
- err += sn9c102_write_reg(cam, 0x50, 0x25);
- err += sn9c102_write_reg(cam, 0x60, 0x26);
- err += sn9c102_write_reg(cam, 0x70, 0x27);
- err += sn9c102_write_reg(cam, 0x80, 0x28);
- err += sn9c102_write_reg(cam, 0x90, 0x29);
- err += sn9c102_write_reg(cam, 0xa0, 0x2a);
- err += sn9c102_write_reg(cam, 0xb0, 0x2b);
- err += sn9c102_write_reg(cam, 0xc0, 0x2c);
- err += sn9c102_write_reg(cam, 0xd0, 0x2d);
- err += sn9c102_write_reg(cam, 0xe0, 0x2e);
- err += sn9c102_write_reg(cam, 0xf0, 0x2f);
- err += sn9c102_write_reg(cam, 0xff, 0x30);
+ err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03},
+ {0x1a, 0x04}, {0x20, 0x05},
+ {0x20, 0x06}, {0x20, 0x07},
+ {0x03, 0x10}, {0x0a, 0x14},
+ {0x60, 0x17}, {0x0f, 0x18},
+ {0x50, 0x19}, {0x1d, 0x1a},
+ {0x10, 0x1b}, {0x02, 0x1c},
+ {0x03, 0x1d}, {0x0f, 0x1e},
+ {0x0c, 0x1f}, {0x00, 0x20},
+ {0x10, 0x21}, {0x20, 0x22},
+ {0x30, 0x23}, {0x40, 0x24},
+ {0x50, 0x25}, {0x60, 0x26},
+ {0x70, 0x27}, {0x80, 0x28},
+ {0x90, 0x29}, {0xa0, 0x2a},
+ {0xb0, 0x2b}, {0xc0, 0x2c},
+ {0xd0, 0x2d}, {0xe0, 0x2e},
+ {0xf0, 0x2f}, {0xff, 0x30});
err += sn9c102_i2c_write(cam, 0x12, 0x8d);
err += sn9c102_i2c_write(cam, 0x12, 0x0d);
@@ -108,23 +88,23 @@ static int ov7630_init(struct sn9c102_device* cam)
err += sn9c102_i2c_write(cam, 0x11, 0x01);
err += sn9c102_i2c_write(cam, 0x1b, 0x04);
err += sn9c102_i2c_write(cam, 0x20, 0x44);
- err += sn9c102_i2c_write(cam, 0x23, 0xee);
- err += sn9c102_i2c_write(cam, 0x26, 0xa0);
- err += sn9c102_i2c_write(cam, 0x27, 0x9a);
+ err += sn9c102_i2c_write(cam, 0x23, 0xee);
+ err += sn9c102_i2c_write(cam, 0x26, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x27, 0x9a);
err += sn9c102_i2c_write(cam, 0x28, 0x20);
- err += sn9c102_i2c_write(cam, 0x29, 0x30);
- err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
- err += sn9c102_i2c_write(cam, 0x30, 0x24);
- err += sn9c102_i2c_write(cam, 0x32, 0x86);
- err += sn9c102_i2c_write(cam, 0x60, 0xa9);
- err += sn9c102_i2c_write(cam, 0x61, 0x42);
- err += sn9c102_i2c_write(cam, 0x65, 0x00);
- err += sn9c102_i2c_write(cam, 0x69, 0x38);
- err += sn9c102_i2c_write(cam, 0x6f, 0x88);
- err += sn9c102_i2c_write(cam, 0x70, 0x0b);
- err += sn9c102_i2c_write(cam, 0x71, 0x00);
- err += sn9c102_i2c_write(cam, 0x74, 0x21);
- err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
+ err += sn9c102_i2c_write(cam, 0x29, 0x30);
+ err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
+ err += sn9c102_i2c_write(cam, 0x30, 0x24);
+ err += sn9c102_i2c_write(cam, 0x32, 0x86);
+ err += sn9c102_i2c_write(cam, 0x60, 0xa9);
+ err += sn9c102_i2c_write(cam, 0x61, 0x42);
+ err += sn9c102_i2c_write(cam, 0x65, 0x00);
+ err += sn9c102_i2c_write(cam, 0x69, 0x38);
+ err += sn9c102_i2c_write(cam, 0x6f, 0x88);
+ err += sn9c102_i2c_write(cam, 0x70, 0x0b);
+ err += sn9c102_i2c_write(cam, 0x71, 0x00);
+ err += sn9c102_i2c_write(cam, 0x74, 0x21);
+ err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
break;
default:
break;
@@ -428,15 +408,14 @@ int sn9c102_probe_ov7630(struct sn9c102_device* cam)
switch (sn9c102_get_bridge(cam)) {
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
- err += sn9c102_write_reg(cam, 0x01, 0x01);
- err += sn9c102_write_reg(cam, 0x00, 0x01);
- err += sn9c102_write_reg(cam, 0x28, 0x17);
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01},
+ {0x00, 0x01}, {0x28, 0x17});
+
break;
case BRIDGE_SN9C103: /* do _not_ change anything! */
- err += sn9c102_write_reg(cam, 0x09, 0x01);
- err += sn9c102_write_reg(cam, 0x42, 0x01);
- err += sn9c102_write_reg(cam, 0x28, 0x17);
- err += sn9c102_write_reg(cam, 0x44, 0x02);
+ err = sn9c102_write_const_regs(cam, {0x09, 0x01},
+ {0x42, 0x01}, {0x28, 0x17},
+ {0x44, 0x02});
pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a);
if (err || pid < 0) { /* try a different initialization */
err = sn9c102_write_reg(cam, 0x01, 0x01);
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c
index d670c24d4435..c898e948fe8d 100644
--- a/drivers/media/video/sn9c102/sn9c102_ov7660.c
+++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c
@@ -22,160 +22,84 @@
#include "sn9c102_sensor.h"
-static struct sn9c102_sensor ov7660;
-
-
static int ov7660_init(struct sn9c102_device* cam)
{
int err = 0;
- err += sn9c102_write_reg(cam, 0x40, 0x02);
- err += sn9c102_write_reg(cam, 0x00, 0x03);
- err += sn9c102_write_reg(cam, 0x1a, 0x04);
- err += sn9c102_write_reg(cam, 0x03, 0x10);
- err += sn9c102_write_reg(cam, 0x08, 0x14);
- err += sn9c102_write_reg(cam, 0x20, 0x17);
- err += sn9c102_write_reg(cam, 0x8b, 0x18);
- err += sn9c102_write_reg(cam, 0x00, 0x19);
- err += sn9c102_write_reg(cam, 0x1d, 0x1a);
- err += sn9c102_write_reg(cam, 0x10, 0x1b);
- err += sn9c102_write_reg(cam, 0x02, 0x1c);
- err += sn9c102_write_reg(cam, 0x03, 0x1d);
- err += sn9c102_write_reg(cam, 0x0f, 0x1e);
- err += sn9c102_write_reg(cam, 0x0c, 0x1f);
- err += sn9c102_write_reg(cam, 0x00, 0x20);
- err += sn9c102_write_reg(cam, 0x29, 0x21);
- err += sn9c102_write_reg(cam, 0x40, 0x22);
- err += sn9c102_write_reg(cam, 0x54, 0x23);
- err += sn9c102_write_reg(cam, 0x66, 0x24);
- err += sn9c102_write_reg(cam, 0x76, 0x25);
- err += sn9c102_write_reg(cam, 0x85, 0x26);
- err += sn9c102_write_reg(cam, 0x94, 0x27);
- err += sn9c102_write_reg(cam, 0xa1, 0x28);
- err += sn9c102_write_reg(cam, 0xae, 0x29);
- err += sn9c102_write_reg(cam, 0xbb, 0x2a);
- err += sn9c102_write_reg(cam, 0xc7, 0x2b);
- err += sn9c102_write_reg(cam, 0xd3, 0x2c);
- err += sn9c102_write_reg(cam, 0xde, 0x2d);
- err += sn9c102_write_reg(cam, 0xea, 0x2e);
- err += sn9c102_write_reg(cam, 0xf4, 0x2f);
- err += sn9c102_write_reg(cam, 0xff, 0x30);
- err += sn9c102_write_reg(cam, 0x00, 0x3F);
- err += sn9c102_write_reg(cam, 0xC7, 0x40);
- err += sn9c102_write_reg(cam, 0x01, 0x41);
- err += sn9c102_write_reg(cam, 0x44, 0x42);
- err += sn9c102_write_reg(cam, 0x00, 0x43);
- err += sn9c102_write_reg(cam, 0x44, 0x44);
- err += sn9c102_write_reg(cam, 0x00, 0x45);
- err += sn9c102_write_reg(cam, 0x44, 0x46);
- err += sn9c102_write_reg(cam, 0x00, 0x47);
- err += sn9c102_write_reg(cam, 0xC7, 0x48);
- err += sn9c102_write_reg(cam, 0x01, 0x49);
- err += sn9c102_write_reg(cam, 0xC7, 0x4A);
- err += sn9c102_write_reg(cam, 0x01, 0x4B);
- err += sn9c102_write_reg(cam, 0xC7, 0x4C);
- err += sn9c102_write_reg(cam, 0x01, 0x4D);
- err += sn9c102_write_reg(cam, 0x44, 0x4E);
- err += sn9c102_write_reg(cam, 0x00, 0x4F);
- err += sn9c102_write_reg(cam, 0x44, 0x50);
- err += sn9c102_write_reg(cam, 0x00, 0x51);
- err += sn9c102_write_reg(cam, 0x44, 0x52);
- err += sn9c102_write_reg(cam, 0x00, 0x53);
- err += sn9c102_write_reg(cam, 0xC7, 0x54);
- err += sn9c102_write_reg(cam, 0x01, 0x55);
- err += sn9c102_write_reg(cam, 0xC7, 0x56);
- err += sn9c102_write_reg(cam, 0x01, 0x57);
- err += sn9c102_write_reg(cam, 0xC7, 0x58);
- err += sn9c102_write_reg(cam, 0x01, 0x59);
- err += sn9c102_write_reg(cam, 0x44, 0x5A);
- err += sn9c102_write_reg(cam, 0x00, 0x5B);
- err += sn9c102_write_reg(cam, 0x44, 0x5C);
- err += sn9c102_write_reg(cam, 0x00, 0x5D);
- err += sn9c102_write_reg(cam, 0x44, 0x5E);
- err += sn9c102_write_reg(cam, 0x00, 0x5F);
- err += sn9c102_write_reg(cam, 0xC7, 0x60);
- err += sn9c102_write_reg(cam, 0x01, 0x61);
- err += sn9c102_write_reg(cam, 0xC7, 0x62);
- err += sn9c102_write_reg(cam, 0x01, 0x63);
- err += sn9c102_write_reg(cam, 0xC7, 0x64);
- err += sn9c102_write_reg(cam, 0x01, 0x65);
- err += sn9c102_write_reg(cam, 0x44, 0x66);
- err += sn9c102_write_reg(cam, 0x00, 0x67);
- err += sn9c102_write_reg(cam, 0x44, 0x68);
- err += sn9c102_write_reg(cam, 0x00, 0x69);
- err += sn9c102_write_reg(cam, 0x44, 0x6A);
- err += sn9c102_write_reg(cam, 0x00, 0x6B);
- err += sn9c102_write_reg(cam, 0xC7, 0x6C);
- err += sn9c102_write_reg(cam, 0x01, 0x6D);
- err += sn9c102_write_reg(cam, 0xC7, 0x6E);
- err += sn9c102_write_reg(cam, 0x01, 0x6F);
- err += sn9c102_write_reg(cam, 0xC7, 0x70);
- err += sn9c102_write_reg(cam, 0x01, 0x71);
- err += sn9c102_write_reg(cam, 0x44, 0x72);
- err += sn9c102_write_reg(cam, 0x00, 0x73);
- err += sn9c102_write_reg(cam, 0x44, 0x74);
- err += sn9c102_write_reg(cam, 0x00, 0x75);
- err += sn9c102_write_reg(cam, 0x44, 0x76);
- err += sn9c102_write_reg(cam, 0x00, 0x77);
- err += sn9c102_write_reg(cam, 0xC7, 0x78);
- err += sn9c102_write_reg(cam, 0x01, 0x79);
- err += sn9c102_write_reg(cam, 0xC7, 0x7A);
- err += sn9c102_write_reg(cam, 0x01, 0x7B);
- err += sn9c102_write_reg(cam, 0xC7, 0x7C);
- err += sn9c102_write_reg(cam, 0x01, 0x7D);
- err += sn9c102_write_reg(cam, 0x44, 0x7E);
- err += sn9c102_write_reg(cam, 0x00, 0x7F);
- err += sn9c102_write_reg(cam, 0x14, 0x84);
- err += sn9c102_write_reg(cam, 0x00, 0x85);
- err += sn9c102_write_reg(cam, 0x27, 0x86);
- err += sn9c102_write_reg(cam, 0x00, 0x87);
- err += sn9c102_write_reg(cam, 0x07, 0x88);
- err += sn9c102_write_reg(cam, 0x00, 0x89);
- err += sn9c102_write_reg(cam, 0xEC, 0x8A);
- err += sn9c102_write_reg(cam, 0x0f, 0x8B);
- err += sn9c102_write_reg(cam, 0xD8, 0x8C);
- err += sn9c102_write_reg(cam, 0x0f, 0x8D);
- err += sn9c102_write_reg(cam, 0x3D, 0x8E);
- err += sn9c102_write_reg(cam, 0x00, 0x8F);
- err += sn9c102_write_reg(cam, 0x3D, 0x90);
- err += sn9c102_write_reg(cam, 0x00, 0x91);
- err += sn9c102_write_reg(cam, 0xCD, 0x92);
- err += sn9c102_write_reg(cam, 0x0f, 0x93);
- err += sn9c102_write_reg(cam, 0xf7, 0x94);
- err += sn9c102_write_reg(cam, 0x0f, 0x95);
- err += sn9c102_write_reg(cam, 0x0C, 0x96);
- err += sn9c102_write_reg(cam, 0x00, 0x97);
- err += sn9c102_write_reg(cam, 0x00, 0x98);
- err += sn9c102_write_reg(cam, 0x66, 0x99);
- err += sn9c102_write_reg(cam, 0x05, 0x9A);
- err += sn9c102_write_reg(cam, 0x00, 0x9B);
- err += sn9c102_write_reg(cam, 0x04, 0x9C);
- err += sn9c102_write_reg(cam, 0x00, 0x9D);
- err += sn9c102_write_reg(cam, 0x08, 0x9E);
- err += sn9c102_write_reg(cam, 0x00, 0x9F);
- err += sn9c102_write_reg(cam, 0x2D, 0xC0);
- err += sn9c102_write_reg(cam, 0x2D, 0xC1);
- err += sn9c102_write_reg(cam, 0x3A, 0xC2);
- err += sn9c102_write_reg(cam, 0x05, 0xC3);
- err += sn9c102_write_reg(cam, 0x04, 0xC4);
- err += sn9c102_write_reg(cam, 0x3F, 0xC5);
- err += sn9c102_write_reg(cam, 0x00, 0xC6);
- err += sn9c102_write_reg(cam, 0x00, 0xC7);
- err += sn9c102_write_reg(cam, 0x50, 0xC8);
- err += sn9c102_write_reg(cam, 0x3C, 0xC9);
- err += sn9c102_write_reg(cam, 0x28, 0xCA);
- err += sn9c102_write_reg(cam, 0xD8, 0xCB);
- err += sn9c102_write_reg(cam, 0x14, 0xCC);
- err += sn9c102_write_reg(cam, 0xEC, 0xCD);
- err += sn9c102_write_reg(cam, 0x32, 0xCE);
- err += sn9c102_write_reg(cam, 0xDD, 0xCF);
- err += sn9c102_write_reg(cam, 0x32, 0xD0);
- err += sn9c102_write_reg(cam, 0xDD, 0xD1);
- err += sn9c102_write_reg(cam, 0x6A, 0xD2);
- err += sn9c102_write_reg(cam, 0x50, 0xD3);
- err += sn9c102_write_reg(cam, 0x00, 0xD4);
- err += sn9c102_write_reg(cam, 0x00, 0xD5);
- err += sn9c102_write_reg(cam, 0x00, 0xD6);
+ err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03},
+ {0x1a, 0x04}, {0x03, 0x10},
+ {0x08, 0x14}, {0x20, 0x17},
+ {0x8b, 0x18}, {0x00, 0x19},
+ {0x1d, 0x1a}, {0x10, 0x1b},
+ {0x02, 0x1c}, {0x03, 0x1d},
+ {0x0f, 0x1e}, {0x0c, 0x1f},
+ {0x00, 0x20}, {0x29, 0x21},
+ {0x40, 0x22}, {0x54, 0x23},
+ {0x66, 0x24}, {0x76, 0x25},
+ {0x85, 0x26}, {0x94, 0x27},
+ {0xa1, 0x28}, {0xae, 0x29},
+ {0xbb, 0x2a}, {0xc7, 0x2b},
+ {0xd3, 0x2c}, {0xde, 0x2d},
+ {0xea, 0x2e}, {0xf4, 0x2f},
+ {0xff, 0x30}, {0x00, 0x3F},
+ {0xC7, 0x40}, {0x01, 0x41},
+ {0x44, 0x42}, {0x00, 0x43},
+ {0x44, 0x44}, {0x00, 0x45},
+ {0x44, 0x46}, {0x00, 0x47},
+ {0xC7, 0x48}, {0x01, 0x49},
+ {0xC7, 0x4A}, {0x01, 0x4B},
+ {0xC7, 0x4C}, {0x01, 0x4D},
+ {0x44, 0x4E}, {0x00, 0x4F},
+ {0x44, 0x50}, {0x00, 0x51},
+ {0x44, 0x52}, {0x00, 0x53},
+ {0xC7, 0x54}, {0x01, 0x55},
+ {0xC7, 0x56}, {0x01, 0x57},
+ {0xC7, 0x58}, {0x01, 0x59},
+ {0x44, 0x5A}, {0x00, 0x5B},
+ {0x44, 0x5C}, {0x00, 0x5D},
+ {0x44, 0x5E}, {0x00, 0x5F},
+ {0xC7, 0x60}, {0x01, 0x61},
+ {0xC7, 0x62}, {0x01, 0x63},
+ {0xC7, 0x64}, {0x01, 0x65},
+ {0x44, 0x66}, {0x00, 0x67},
+ {0x44, 0x68}, {0x00, 0x69},
+ {0x44, 0x6A}, {0x00, 0x6B},
+ {0xC7, 0x6C}, {0x01, 0x6D},
+ {0xC7, 0x6E}, {0x01, 0x6F},
+ {0xC7, 0x70}, {0x01, 0x71},
+ {0x44, 0x72}, {0x00, 0x73},
+ {0x44, 0x74}, {0x00, 0x75},
+ {0x44, 0x76}, {0x00, 0x77},
+ {0xC7, 0x78}, {0x01, 0x79},
+ {0xC7, 0x7A}, {0x01, 0x7B},
+ {0xC7, 0x7C}, {0x01, 0x7D},
+ {0x44, 0x7E}, {0x00, 0x7F},
+ {0x14, 0x84}, {0x00, 0x85},
+ {0x27, 0x86}, {0x00, 0x87},
+ {0x07, 0x88}, {0x00, 0x89},
+ {0xEC, 0x8A}, {0x0f, 0x8B},
+ {0xD8, 0x8C}, {0x0f, 0x8D},
+ {0x3D, 0x8E}, {0x00, 0x8F},
+ {0x3D, 0x90}, {0x00, 0x91},
+ {0xCD, 0x92}, {0x0f, 0x93},
+ {0xf7, 0x94}, {0x0f, 0x95},
+ {0x0C, 0x96}, {0x00, 0x97},
+ {0x00, 0x98}, {0x66, 0x99},
+ {0x05, 0x9A}, {0x00, 0x9B},
+ {0x04, 0x9C}, {0x00, 0x9D},
+ {0x08, 0x9E}, {0x00, 0x9F},
+ {0x2D, 0xC0}, {0x2D, 0xC1},
+ {0x3A, 0xC2}, {0x05, 0xC3},
+ {0x04, 0xC4}, {0x3F, 0xC5},
+ {0x00, 0xC6}, {0x00, 0xC7},
+ {0x50, 0xC8}, {0x3C, 0xC9},
+ {0x28, 0xCA}, {0xD8, 0xCB},
+ {0x14, 0xCC}, {0xEC, 0xCD},
+ {0x32, 0xCE}, {0xDD, 0xCF},
+ {0x32, 0xD0}, {0xDD, 0xD1},
+ {0x6A, 0xD2}, {0x50, 0xD3},
+ {0x00, 0xD4}, {0x00, 0xD5},
+ {0x00, 0xD6});
err += sn9c102_i2c_write(cam, 0x12, 0x80);
err += sn9c102_i2c_write(cam, 0x11, 0x09);
@@ -572,13 +496,11 @@ static struct sn9c102_sensor ov7660 = {
int sn9c102_probe_ov7660(struct sn9c102_device* cam)
{
- int pid, ver, err = 0;
+ int pid, ver, err;
- err += sn9c102_write_reg(cam, 0x01, 0xf1);
- err += sn9c102_write_reg(cam, 0x00, 0xf1);
- err += sn9c102_write_reg(cam, 0x01, 0x01);
- err += sn9c102_write_reg(cam, 0x00, 0x01);
- err += sn9c102_write_reg(cam, 0x28, 0x17);
+ err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
+ {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17});
pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a);
ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b);
diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c
index 8d79a5fae5de..67151964801f 100644
--- a/drivers/media/video/sn9c102/sn9c102_pas106b.c
+++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c
@@ -23,19 +23,13 @@
#include "sn9c102_sensor.h"
-static struct sn9c102_sensor pas106b;
-
-
static int pas106b_init(struct sn9c102_device* cam)
{
int err = 0;
- err += sn9c102_write_reg(cam, 0x00, 0x10);
- err += sn9c102_write_reg(cam, 0x00, 0x11);
- err += sn9c102_write_reg(cam, 0x00, 0x14);
- err += sn9c102_write_reg(cam, 0x20, 0x17);
- err += sn9c102_write_reg(cam, 0x20, 0x19);
- err += sn9c102_write_reg(cam, 0x09, 0x18);
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x00, 0x14}, {0x20, 0x17},
+ {0x20, 0x19}, {0x09, 0x18});
err += sn9c102_i2c_write(cam, 0x02, 0x0c);
err += sn9c102_i2c_write(cam, 0x05, 0x5a);
@@ -172,7 +166,7 @@ static int pas106b_set_pix_format(struct sn9c102_device* cam,
static struct sn9c102_sensor pas106b = {
.name = "PAS106B",
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
- .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
.sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
.frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_2WIRES,
@@ -279,16 +273,17 @@ static struct sn9c102_sensor pas106b = {
int sn9c102_probe_pas106b(struct sn9c102_device* cam)
{
- int r0 = 0, r1 = 0, err = 0;
+ int r0 = 0, r1 = 0, err;
unsigned int pid = 0;
/*
Minimal initialization to enable the I2C communication
NOTE: do NOT change the values!
*/
- err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
- err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
- err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
+ err = sn9c102_write_const_regs(cam,
+ {0x01, 0x01}, /* sensor power down */
+ {0x00, 0x01}, /* sensor power on */
+ {0x28, 0x17});/* sensor clock 24 MHz */
if (err)
return -EIO;
diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
index 7894f01b56e8..c1b8d6b63b47 100644
--- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
+++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
@@ -28,9 +28,6 @@
#include "sn9c102_sensor.h"
-static struct sn9c102_sensor pas202bcb;
-
-
static int pas202bcb_init(struct sn9c102_device* cam)
{
int err = 0;
@@ -38,47 +35,29 @@ static int pas202bcb_init(struct sn9c102_device* cam)
switch (sn9c102_get_bridge(cam)) {
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
- err += sn9c102_write_reg(cam, 0x00, 0x10);
- err += sn9c102_write_reg(cam, 0x00, 0x11);
- err += sn9c102_write_reg(cam, 0x00, 0x14);
- err += sn9c102_write_reg(cam, 0x20, 0x17);
- err += sn9c102_write_reg(cam, 0x30, 0x19);
- err += sn9c102_write_reg(cam, 0x09, 0x18);
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10},
+ {0x00, 0x11}, {0x00, 0x14},
+ {0x20, 0x17}, {0x30, 0x19},
+ {0x09, 0x18});
break;
case BRIDGE_SN9C103:
- err += sn9c102_write_reg(cam, 0x00, 0x02);
- err += sn9c102_write_reg(cam, 0x00, 0x03);
- err += sn9c102_write_reg(cam, 0x1a, 0x04);
- err += sn9c102_write_reg(cam, 0x20, 0x05);
- err += sn9c102_write_reg(cam, 0x20, 0x06);
- err += sn9c102_write_reg(cam, 0x20, 0x07);
- err += sn9c102_write_reg(cam, 0x00, 0x10);
- err += sn9c102_write_reg(cam, 0x00, 0x11);
- err += sn9c102_write_reg(cam, 0x00, 0x14);
- err += sn9c102_write_reg(cam, 0x20, 0x17);
- err += sn9c102_write_reg(cam, 0x30, 0x19);
- err += sn9c102_write_reg(cam, 0x09, 0x18);
- err += sn9c102_write_reg(cam, 0x02, 0x1c);
- err += sn9c102_write_reg(cam, 0x03, 0x1d);
- err += sn9c102_write_reg(cam, 0x0f, 0x1e);
- err += sn9c102_write_reg(cam, 0x0c, 0x1f);
- err += sn9c102_write_reg(cam, 0x00, 0x20);
- err += sn9c102_write_reg(cam, 0x10, 0x21);
- err += sn9c102_write_reg(cam, 0x20, 0x22);
- err += sn9c102_write_reg(cam, 0x30, 0x23);
- err += sn9c102_write_reg(cam, 0x40, 0x24);
- err += sn9c102_write_reg(cam, 0x50, 0x25);
- err += sn9c102_write_reg(cam, 0x60, 0x26);
- err += sn9c102_write_reg(cam, 0x70, 0x27);
- err += sn9c102_write_reg(cam, 0x80, 0x28);
- err += sn9c102_write_reg(cam, 0x90, 0x29);
- err += sn9c102_write_reg(cam, 0xa0, 0x2a);
- err += sn9c102_write_reg(cam, 0xb0, 0x2b);
- err += sn9c102_write_reg(cam, 0xc0, 0x2c);
- err += sn9c102_write_reg(cam, 0xd0, 0x2d);
- err += sn9c102_write_reg(cam, 0xe0, 0x2e);
- err += sn9c102_write_reg(cam, 0xf0, 0x2f);
- err += sn9c102_write_reg(cam, 0xff, 0x30);
+ err = sn9c102_write_const_regs(cam, {0x00, 0x02},
+ {0x00, 0x03}, {0x1a, 0x04},
+ {0x20, 0x05}, {0x20, 0x06},
+ {0x20, 0x07}, {0x00, 0x10},
+ {0x00, 0x11}, {0x00, 0x14},
+ {0x20, 0x17}, {0x30, 0x19},
+ {0x09, 0x18}, {0x02, 0x1c},
+ {0x03, 0x1d}, {0x0f, 0x1e},
+ {0x0c, 0x1f}, {0x00, 0x20},
+ {0x10, 0x21}, {0x20, 0x22},
+ {0x30, 0x23}, {0x40, 0x24},
+ {0x50, 0x25}, {0x60, 0x26},
+ {0x70, 0x27}, {0x80, 0x28},
+ {0x90, 0x29}, {0xa0, 0x2a},
+ {0xb0, 0x2b}, {0xc0, 0x2c},
+ {0xd0, 0x2d}, {0xe0, 0x2e},
+ {0xf0, 0x2f}, {0xff, 0x30});
break;
default:
break;
@@ -328,15 +307,15 @@ int sn9c102_probe_pas202bcb(struct sn9c102_device* cam)
switch (sn9c102_get_bridge(cam)) {
case BRIDGE_SN9C101:
case BRIDGE_SN9C102:
- err += sn9c102_write_reg(cam, 0x01, 0x01); /* power down */
- err += sn9c102_write_reg(cam, 0x40, 0x01); /* power on */
- err += sn9c102_write_reg(cam, 0x28, 0x17); /* clock 24 MHz */
+ err = sn9c102_write_const_regs(cam,
+ {0x01, 0x01}, /* power down */
+ {0x40, 0x01}, /* power on */
+ {0x28, 0x17});/* clock 24 MHz */
break;
case BRIDGE_SN9C103: /* do _not_ change anything! */
- err += sn9c102_write_reg(cam, 0x09, 0x01);
- err += sn9c102_write_reg(cam, 0x44, 0x01);
- err += sn9c102_write_reg(cam, 0x44, 0x02);
- err += sn9c102_write_reg(cam, 0x29, 0x17);
+ err = sn9c102_write_const_regs(cam, {0x09, 0x01},
+ {0x44, 0x01}, {0x44, 0x02},
+ {0x29, 0x17});
break;
default:
break;
diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h
index 05f2942639c3..1bbf64c897a2 100644
--- a/drivers/media/video/sn9c102/sn9c102_sensor.h
+++ b/drivers/media/video/sn9c102/sn9c102_sensor.h
@@ -114,9 +114,17 @@ extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value);
extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address);
/* I/O on registers in the bridge. Could be used by the sensor methods too */
-extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index);
-extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index);
+extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
+extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2],
+ int count);
+/*
+ * Write multiple registers with constant values. For example:
+ * sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18});
+ */
+#define sn9c102_write_const_regs(device, data...) \
+ ({ const static u8 _data[][2] = {data}; \
+ sn9c102_write_regs(device, _data, ARRAY_SIZE(_data)); })
/*****************************************************************************/
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
index 90023ad63adc..0e7ec8662c70 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
@@ -22,21 +22,14 @@
#include "sn9c102_sensor.h"
-static struct sn9c102_sensor tas5110c1b;
-
-
static int tas5110c1b_init(struct sn9c102_device* cam)
{
int err = 0;
- err += sn9c102_write_reg(cam, 0x01, 0x01);
- err += sn9c102_write_reg(cam, 0x44, 0x01);
- err += sn9c102_write_reg(cam, 0x00, 0x10);
- err += sn9c102_write_reg(cam, 0x00, 0x11);
- err += sn9c102_write_reg(cam, 0x0a, 0x14);
- err += sn9c102_write_reg(cam, 0x60, 0x17);
- err += sn9c102_write_reg(cam, 0x06, 0x18);
- err += sn9c102_write_reg(cam, 0xfb, 0x19);
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01},
+ {0x00, 0x10}, {0x00, 0x11},
+ {0x0a, 0x14}, {0x60, 0x17},
+ {0x06, 0x18}, {0xfb, 0x19});
err += sn9c102_i2c_write(cam, 0xc0, 0x80);
@@ -98,7 +91,7 @@ static int tas5110c1b_set_pix_format(struct sn9c102_device* cam,
static struct sn9c102_sensor tas5110c1b = {
.name = "TAS5110C1B",
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
- .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
.sysfs_ops = SN9C102_I2C_WRITE,
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
@@ -146,7 +139,6 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
const struct usb_device_id tas5110c1b_id_table[] = {
{ USB_DEVICE(0x0c45, 0x6001), },
{ USB_DEVICE(0x0c45, 0x6005), },
- { USB_DEVICE(0x0c45, 0x6007), },
{ USB_DEVICE(0x0c45, 0x60ab), },
{ }
};
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/video/sn9c102/sn9c102_tas5110d.c
new file mode 100644
index 000000000000..83a39e8b5e71
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110d.c
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * 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 "sn9c102_sensor.h"
+
+
+static int tas5110d_init(struct sn9c102_device* cam)
+{
+ int err;
+
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01},
+ {0x0a, 0x14}, {0x60, 0x17},
+ {0x06, 0x18}, {0xfb, 0x19});
+
+ err += sn9c102_i2c_write(cam, 0x9a, 0xca);
+
+ return err;
+}
+
+
+static int tas5110d_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ err += sn9c102_write_reg(cam, 0x14, 0x1a);
+ err += sn9c102_write_reg(cam, 0x0a, 0x1b);
+
+ return err;
+}
+
+
+static int tas5110d_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x3b, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0xfb, 0x19);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor tas5110d = {
+ .name = "TAS5110D",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
+ .sysfs_ops = SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x61,
+ .init = &tas5110d_init,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ },
+ .set_crop = &tas5110d_set_crop,
+ .pix_format = {
+ .width = 352,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &tas5110d_set_pix_format
+};
+
+
+int sn9c102_probe_tas5110d(struct sn9c102_device* cam)
+{
+ const struct usb_device_id tas5110d_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6007), },
+ { }
+ };
+
+ if (!sn9c102_match_id(cam, tas5110d_id_table))
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &tas5110d);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
index cb1b318bc1ff..50406503fc40 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
+++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
@@ -22,21 +22,14 @@
#include "sn9c102_sensor.h"
-static struct sn9c102_sensor tas5130d1b;
-
-
static int tas5130d1b_init(struct sn9c102_device* cam)
{
- int err = 0;
+ int err;
- err += sn9c102_write_reg(cam, 0x01, 0x01);
- err += sn9c102_write_reg(cam, 0x20, 0x17);
- err += sn9c102_write_reg(cam, 0x04, 0x01);
- err += sn9c102_write_reg(cam, 0x01, 0x10);
- err += sn9c102_write_reg(cam, 0x00, 0x11);
- err += sn9c102_write_reg(cam, 0x00, 0x14);
- err += sn9c102_write_reg(cam, 0x60, 0x17);
- err += sn9c102_write_reg(cam, 0x07, 0x18);
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17},
+ {0x04, 0x01}, {0x01, 0x10},
+ {0x00, 0x11}, {0x00, 0x14},
+ {0x60, 0x17}, {0x07, 0x18});
return err;
}
@@ -99,7 +92,7 @@ static int tas5130d1b_set_pix_format(struct sn9c102_device* cam,
static struct sn9c102_sensor tas5130d1b = {
.name = "TAS5130D1B",
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
- .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
.sysfs_ops = SN9C102_I2C_WRITE,
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index d1ccc064206f..43225802a551 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -45,7 +45,6 @@
#include <linux/slab.h>
#include <linux/videodev.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <media/v4l2-common.h>
#include <media/i2c-addr.h>
diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c
index 027c8a074dfe..1a1bef0e9c3d 100644
--- a/drivers/media/video/tda8290.c
+++ b/drivers/media/video/tda8290.c
@@ -192,14 +192,52 @@ static struct tda827xa_data tda827xa_analog[] = {
{ .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} /* End */
};
+static void tda827xa_lna_gain(struct i2c_client *c, int high)
+{
+ struct tuner *t = i2c_get_clientdata(c);
+ unsigned char buf[] = {0x22, 0x01};
+ int arg;
+ struct i2c_msg msg = {.addr = c->addr, .flags = 0, .buf = buf, .len = sizeof(buf)};
+ if (t->config) {
+ if (high)
+ tuner_dbg("setting LNA to high gain\n");
+ else
+ tuner_dbg("setting LNA to low gain\n");
+ }
+ switch (t->config) {
+ case 0: /* no LNA */
+ break;
+ case 1: /* switch is GPIO 0 of tda8290 */
+ case 2:
+ /* turn Vsync on */
+ if (t->std & V4L2_STD_MN)
+ arg = 1;
+ else
+ arg = 0;
+ if (t->tuner_callback)
+ t->tuner_callback(c->adapter->algo_data, 1, arg);
+ buf[1] = high ? 0 : 1;
+ if (t->config == 2)
+ buf[1] = high ? 1 : 0;
+ i2c_transfer(c->adapter, &msg, 1);
+ break;
+ case 3: /* switch with GPIO of saa713x */
+ if (t->tuner_callback)
+ t->tuner_callback(c->adapter->algo_data, 0, high);
+ break;
+ }
+}
+
static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq)
{
- unsigned char tuner_reg[14];
- unsigned char reg2[2];
+ unsigned char tuner_reg[11];
u32 N;
int i;
struct tuner *t = i2c_get_clientdata(c);
- struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0};
+ struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0, .buf = tuner_reg};
+
+ tda827xa_lna_gain( c, 1);
+ msleep(10);
if (t->mode == V4L2_TUNER_RADIO)
freq = freq / 1000;
@@ -222,48 +260,58 @@ static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq)
tuner_reg[5] = (tda827xa_analog[i].spd << 5) + (tda827xa_analog[i].svco << 3) +
tda827xa_analog[i].sbs;
tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4);
- tuner_reg[7] = 0x0c;
+ tuner_reg[7] = 0x1c;
tuner_reg[8] = 4;
tuner_reg[9] = 0x20;
- tuner_reg[10] = 0xff;
- tuner_reg[11] = 0xe0;
- tuner_reg[12] = 0;
- tuner_reg[13] = 0x39 + (t->tda827x_lpsel << 1);
+ tuner_reg[10] = 0x00;
+ msg.len = 11;
+ i2c_transfer(c->adapter, &msg, 1);
- msg.buf = tuner_reg;
- msg.len = 14;
+ tuner_reg[0] = 0x90;
+ tuner_reg[1] = 0xff;
+ tuner_reg[2] = 0xe0;
+ tuner_reg[3] = 0;
+ tuner_reg[4] = 0x99 + (t->tda827x_lpsel << 1);
+ msg.len = 5;
i2c_transfer(c->adapter, &msg, 1);
- msg.buf= reg2;
+ tuner_reg[0] = 0xa0;
+ tuner_reg[1] = 0xc0;
msg.len = 2;
- reg2[0] = 0x60;
- reg2[1] = 0x3c;
i2c_transfer(c->adapter, &msg, 1);
- reg2[0] = 0xa0;
- reg2[1] = 0xc0;
+ tuner_reg[0] = 0x30;
+ tuner_reg[1] = 0x10 + tda827xa_analog[i].scr;
i2c_transfer(c->adapter, &msg, 1);
- msleep(2);
- reg2[0] = 0x30;
- reg2[1] = 0x10 + tda827xa_analog[i].scr;
+ msg.flags = I2C_M_RD;
+ i2c_transfer(c->adapter, &msg, 1);
+ msg.flags = 0;
+ tuner_reg[1] >>= 4;
+ tuner_dbg("AGC2 gain is: %d\n", tuner_reg[1]);
+ if (tuner_reg[1] < 1)
+ tda827xa_lna_gain( c, 0);
+
+ msleep(100);
+ tuner_reg[0] = 0x60;
+ tuner_reg[1] = 0x3c;
i2c_transfer(c->adapter, &msg, 1);
- msleep(550);
- reg2[0] = 0x50;
- reg2[1] = 0x8f + (tda827xa_analog[i].gc3 << 4);
+ msleep(163);
+ tuner_reg[0] = 0x50;
+ tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4);
i2c_transfer(c->adapter, &msg, 1);
- reg2[0] = 0x80;
- reg2[1] = 0x28;
+ tuner_reg[0] = 0x80;
+ tuner_reg[1] = 0x28;
i2c_transfer(c->adapter, &msg, 1);
- reg2[0] = 0xb0;
- reg2[1] = 0x01;
+ tuner_reg[0] = 0xb0;
+ tuner_reg[1] = 0x01;
i2c_transfer(c->adapter, &msg, 1);
- reg2[0] = 0xc0;
- reg2[1] = 0x19 + (t->tda827x_lpsel << 1);
+ tuner_reg[0] = 0xc0;
+ tuner_reg[1] = 0x19 + (t->tda827x_lpsel << 1);
i2c_transfer(c->adapter, &msg, 1);
}
@@ -319,7 +367,9 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq)
unsigned char addr_pll_stat = 0x1b;
unsigned char adc_sat, agc_stat,
pll_stat;
+ int i;
+ tuner_dbg("tda827xa config is 0x%02x\n", t->config);
i2c_master_send(c, easy_mode, 2);
i2c_master_send(c, agc_out_on, 2);
i2c_master_send(c, soft_reset, 2);
@@ -340,17 +390,22 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq)
tda827xa_tune(c, ifc, freq);
else
tda827x_tune(c, ifc, freq);
+ for (i = 0; i < 3; i++) {
+ i2c_master_send(c, &addr_pll_stat, 1);
+ i2c_master_recv(c, &pll_stat, 1);
+ if (pll_stat & 0x80) {
+ i2c_master_send(c, &addr_adc_sat, 1);
+ i2c_master_recv(c, &adc_sat, 1);
+ i2c_master_send(c, &addr_agc_stat, 1);
+ i2c_master_recv(c, &agc_stat, 1);
+ tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat);
+ break;
+ } else {
+ tuner_dbg("tda8290 not locked, no signal?\n");
+ msleep(100);
+ }
+ }
/* adjust headroom resp. gain */
- i2c_master_send(c, &addr_adc_sat, 1);
- i2c_master_recv(c, &adc_sat, 1);
- i2c_master_send(c, &addr_agc_stat, 1);
- i2c_master_recv(c, &agc_stat, 1);
- i2c_master_send(c, &addr_pll_stat, 1);
- i2c_master_recv(c, &pll_stat, 1);
- if (pll_stat & 0x80)
- tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat);
- else
- tuner_dbg("tda8290 not locked, no signal?\n");
if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) {
tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n",
agc_stat, adc_sat, pll_stat & 0x80);
@@ -407,7 +462,6 @@ static void set_audio(struct tuner *t)
char* mode;
t->tda827x_lpsel = 0;
- mode = "xx";
if (t->std & V4L2_STD_MN) {
t->sgIF = 92;
t->tda8290_easy_mode = 0x01;
@@ -437,8 +491,12 @@ static void set_audio(struct tuner *t)
t->sgIF = 20;
t->tda8290_easy_mode = 0x40;
mode = "LC";
+ } else {
+ t->sgIF = 124;
+ t->tda8290_easy_mode = 0x10;
+ mode = "xx";
}
- tuner_dbg("setting tda8290 to system %s\n", mode);
+ tuner_dbg("setting tda8290 to system %s\n", mode);
}
static void set_tv_freq(struct i2c_client *c, unsigned int freq)
@@ -487,11 +545,16 @@ static void standby(struct i2c_client *c)
static void tda8290_init_if(struct i2c_client *c)
{
+ struct tuner *t = i2c_get_clientdata(c);
unsigned char set_VS[] = { 0x30, 0x6F };
+ unsigned char set_GP00_CF[] = { 0x20, 0x01 };
unsigned char set_GP01_CF[] = { 0x20, 0x0B };
+ if ((t->config == 1) || (t->config == 2))
+ i2c_master_send(c, set_GP00_CF, 2);
+ else
+ i2c_master_send(c, set_GP01_CF, 2);
i2c_master_send(c, set_VS, 2);
- i2c_master_send(c, set_GP01_CF, 2);
}
static void tda8290_init_tuner(struct i2c_client *c)
@@ -576,6 +639,7 @@ int tda8290_init(struct i2c_client *c)
t->has_signal = has_signal;
t->standby = standby;
t->tda827x_lpsel = 0;
+ t->mode = V4L2_TUNER_ANALOG_TV;
tda8290_init_tuner(c);
tda8290_init_if(c);
diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c
index 00f0e8b6e03b..d11044170872 100644
--- a/drivers/media/video/tda9875.c
+++ b/drivers/media/video/tda9875.c
@@ -27,7 +27,6 @@
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 15dbc6bf42a7..505591a7abe9 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -144,7 +144,8 @@ static void set_freq(struct i2c_client *c, unsigned long freq)
}
static void set_type(struct i2c_client *c, unsigned int type,
- unsigned int new_mode_mask)
+ unsigned int new_mode_mask, unsigned int new_config,
+ int (*tuner_callback) (void *dev, int command,int arg))
{
struct tuner *t = i2c_get_clientdata(c);
unsigned char buffer[4];
@@ -159,15 +160,20 @@ static void set_type(struct i2c_client *c, unsigned int type,
return;
}
+ t->type = type;
+ t->config = new_config;
+ if (tuner_callback != NULL) {
+ tuner_dbg("defining GPIO callback\n");
+ t->tuner_callback = tuner_callback;
+ }
+
/* This code detects calls by card attach_inform */
if (NULL == t->i2c.dev.driver) {
tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
- t->type=type;
return;
}
- t->type = type;
switch (t->type) {
case TUNER_MT2032:
microtune_init(c);
@@ -234,10 +240,11 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
tuner_dbg("set addr for type %i\n", t->type);
- if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET &&
- (t->mode_mask & tun_setup->mode_mask)) ||
- tun_setup->addr == c->addr)) {
- set_type(c, tun_setup->type, tun_setup->mode_mask);
+ if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
+ (t->mode_mask & tun_setup->mode_mask))) ||
+ (tun_setup->addr == c->addr)) {
+ set_type(c, tun_setup->type, tun_setup->mode_mask,
+ tun_setup->config, tun_setup->tuner_callback);
}
}
@@ -496,7 +503,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
register_client:
tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
i2c_attach_client (&t->i2c);
- set_type (&t->i2c,t->type, t->mode_mask);
+ set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
return 0;
}
@@ -576,10 +583,11 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
switch (cmd) {
/* --- configuration --- */
case TUNER_SET_TYPE_ADDR:
- tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n",
+ tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
((struct tuner_setup *)arg)->type,
((struct tuner_setup *)arg)->addr,
- ((struct tuner_setup *)arg)->mode_mask);
+ ((struct tuner_setup *)arg)->mode_mask,
+ ((struct tuner_setup *)arg)->config);
set_addr(client, (struct tuner_setup *)arg);
break;
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index d506dfaa45a9..a2da5d2affff 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -25,7 +25,6 @@
#include <linux/slab.h>
#include <linux/videodev.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/kthread.h>
@@ -33,6 +32,7 @@
#include <media/tvaudio.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#include <media/i2c-addr.h>
@@ -1775,6 +1775,9 @@ static int chip_command(struct i2c_client *client,
/* the thread will call checkmode() later */
}
break;
+
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0);
}
return 0;
}
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index 4e7c1fa668d3..a1136da74ba8 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -163,7 +163,7 @@ hauppauge_tuner[] =
/* 60-69 */
{ TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"},
{ TUNER_ABSENT, "LG M001D MK3"},
- { TUNER_ABSENT, "LG S701D MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"},
{ TUNER_ABSENT, "LG M701D MK3"},
{ TUNER_ABSENT, "Temic 4146FM5"},
{ TUNER_ABSENT, "Temic 4136FY5"},
@@ -229,6 +229,36 @@ hauppauge_tuner[] =
/* 120-129 */
{ TUNER_ABSENT, "Xceive XC3028"},
{ TUNER_ABSENT, "Philips FQ1216LME MK5"},
+ { TUNER_ABSENT, "Philips FQD1216LME"},
+ { TUNER_ABSENT, "Conexant CX24118A"},
+ { TUNER_ABSENT, "TCL DMF11WIP"},
+ { TUNER_ABSENT, "TCL MFNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MPE05_2H_E"},
+ { TUNER_ABSENT, "TCL MQNM05_4_U"},
+ { TUNER_ABSENT, "TCL M2523_5NH_E"},
+ /* 130-139 */
+ { TUNER_ABSENT, "TCL M2523_3DBH_E"},
+ { TUNER_ABSENT, "TCL M2523_3DIH_E"},
+ { TUNER_ABSENT, "TCL MFPE05_2_U"},
+ { TUNER_ABSENT, "Philips FMD1216MEX"},
+ { TUNER_ABSENT, "Philips FRH2036B"},
+ { TUNER_ABSENT, "Panasonic ENGF75_01GF"},
+ { TUNER_ABSENT, "MaxLinear MXL5005"},
+ { TUNER_ABSENT, "MaxLinear MXL5003"},
+ { TUNER_ABSENT, "Xceive XC2028"},
+ { TUNER_ABSENT, "Microtune MT2131"},
+ /* 140-149 */
+ { TUNER_ABSENT, "Philips 8275A_8295"},
+ { TUNER_ABSENT, "TCL MF02GIP_5N_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DB_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DI_E"},
+ { TUNER_ABSENT, "Microtune MT2266"},
+ { TUNER_ABSENT, "TCL MF10WPP_4N_E"},
+ { TUNER_ABSENT, "LG TAPQ_H702F"},
+ { TUNER_ABSENT, "TCL M09WPP_4N_E"},
+ { TUNER_ABSENT, "MaxLinear MXL5005_v2"},
+ { TUNER_ABSENT, "Philips 18271_8295"},
};
static struct HAUPPAUGE_AUDIOIC
@@ -280,11 +310,16 @@ audioIC[] =
{AUDIO_CHIP_INTERNAL, "CX883"},
{AUDIO_CHIP_INTERNAL, "CX882"},
{AUDIO_CHIP_INTERNAL, "CX25840"},
- /* 35-38 */
+ /* 35-39 */
{AUDIO_CHIP_INTERNAL, "CX25841"},
{AUDIO_CHIP_INTERNAL, "CX25842"},
{AUDIO_CHIP_INTERNAL, "CX25843"},
{AUDIO_CHIP_INTERNAL, "CX23418"},
+ {AUDIO_CHIP_INTERNAL, "CX23885"},
+ /* 40-42 */
+ {AUDIO_CHIP_INTERNAL, "CX23888"},
+ {AUDIO_CHIP_INTERNAL, "SAA7131"},
+ {AUDIO_CHIP_INTERNAL, "CX23887"},
};
/* This list is supplied by Hauppauge. Thanks! */
@@ -301,8 +336,10 @@ static const char *decoderIC[] = {
"CX880", "CX881", "CX883", "SAA7111", "SAA7113",
/* 25-29 */
"CX882", "TVP5150A", "CX25840", "CX25841", "CX25842",
- /* 30-31 */
- "CX25843", "CX23418",
+ /* 30-34 */
+ "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888",
+ /* 35-37 */
+ "SAA7131", "CX25837", "CX23887"
};
static int hasRadioTuner(int tunerType)
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
index 28d1133a3b7a..0b2a961efd22 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/video/upd64031a.c
@@ -27,6 +27,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#include <media/upd64031a.h>
// --------------------- read registers functions define -----------------------
@@ -179,6 +180,9 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void *
}
#endif
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64031A, 0);
+
default:
break;
}
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
index fe38224150d8..401bd21f46eb 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/video/upd64083.c
@@ -26,6 +26,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#include <media/upd64083.h>
MODULE_DESCRIPTION("uPD64083 driver");
@@ -155,6 +156,10 @@ static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *a
break;
}
#endif
+
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64083, 0);
+
default:
break;
}
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
index d34d8c8b7376..687f026753b2 100644
--- a/drivers/media/video/usbvideo/usbvideo.c
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -628,24 +628,21 @@ EXPORT_SYMBOL(usbvideo_HexDump);
/* ******************************************************************** */
/* XXX: this piece of crap really wants some error handling.. */
-static void usbvideo_ClientIncModCount(struct uvd *uvd)
+static int usbvideo_ClientIncModCount(struct uvd *uvd)
{
if (uvd == NULL) {
err("%s: uvd == NULL", __FUNCTION__);
- return;
+ return -EINVAL;
}
if (uvd->handle == NULL) {
err("%s: uvd->handle == NULL", __FUNCTION__);
- return;
- }
- if (uvd->handle->md_module == NULL) {
- err("%s: uvd->handle->md_module == NULL", __FUNCTION__);
- return;
+ return -EINVAL;
}
if (!try_module_get(uvd->handle->md_module)) {
err("%s: try_module_get() == 0", __FUNCTION__);
- return;
+ return -ENODEV;
}
+ return 0;
}
static void usbvideo_ClientDecModCount(struct uvd *uvd)
@@ -712,8 +709,6 @@ int usbvideo_register(
cams->num_cameras = num_cams;
cams->cam = (struct uvd *) &cams[1];
cams->md_module = md;
- if (cams->md_module == NULL)
- warn("%s: module == NULL!", __FUNCTION__);
mutex_init(&cams->lock); /* to 1 == available */
for (i = 0; i < num_cams; i++) {
@@ -1119,7 +1114,8 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file)
if (uvd->debug > 1)
info("%s($%p)", __FUNCTION__, dev);
- usbvideo_ClientIncModCount(uvd);
+ if (0 < usbvideo_ClientIncModCount(uvd))
+ return -ENODEV;
mutex_lock(&uvd->lock);
if (uvd->user) {
diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c
index a40e5838515b..13f69fe6360d 100644
--- a/drivers/media/video/usbvision/usbvision-cards.c
+++ b/drivers/media/video/usbvision/usbvision-cards.c
@@ -1,6 +1,6 @@
/*
- * USBVISION.H
- * usbvision header file
+ * usbvision-cards.c
+ * usbvision cards definition file
*
* Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
*
@@ -28,129 +28,1060 @@
#include <media/v4l2-dev.h>
#include <media/tuner.h>
#include "usbvision.h"
+#include "usbvision-cards.h"
/* Supported Devices: A table for usbvision.c*/
struct usbvision_device_data_st usbvision_device_data[] = {
- {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"},
- {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"},
- {0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USBView II"},
- {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"},
- {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"},
- {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"},
- {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"},
- {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"},
- {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"},
- {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"},
- {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"},
- {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"},
- {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"},
- {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"},
- {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"},
- {0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"},
- {0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"},
- {0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "},
- {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"},
- {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"},
- {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"},
- {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"},
- {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"},
- {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"},
- {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"},
- {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL)"},
- {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) MODEL 566"},
- {0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) 4D23"},
- {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"},
- {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"},
- {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B281"},
- {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"},
- {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"},
- {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"},
- {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 568"},
- {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"},
- {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"},
- {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB device Model 40219 Rev E189"},
- {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"},
- {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"},
- {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"},
- {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"},
- {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"},
- {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"},
- {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"},
- {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"},
- {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"},
- {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"},
- {0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"},
- {0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"},
- {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"},
- {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"},
- {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"},
- {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"},
- {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"},
- {0x2400, 0x4200, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"},
- {} /* Terminating entry */
+ [XANBOO] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 4,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Xanboo",
+ },
+ [BELKIN_VIDEOBUS_II] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Belkin USB VideoBus II Adapter",
+ },
+ [BELKIN_VIDEOBUS] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Belkin Components USB VideoBus",
+ },
+ [BELKIN_USB_VIDEOBUS_II] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Belkin USB VideoBus II",
+ },
+ [ECHOFX_INTERVIEW_LITE] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 0,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "echoFX InterView Lite",
+ },
+ [USBGEAR_USBG_V1] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "USBGear USBG-V1 resp. HAMA USB",
+ },
+ [D_LINK_V100] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 4,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 0,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "D-Link V100",
+ },
+ [X10_USB_CAMERA] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "X10 USB Camera",
+ },
+ [HPG_WINTV_LIVE_PAL_BG] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = -1,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Live (PAL B/G)",
+ },
+ [HPG_WINTV_LIVE_PRO_NTSC_MN] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 0,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Live Pro (NTSC M/N)",
+ },
+ [ZORAN_PMD_NOGATECH] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 2,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan",
+ },
+ [NOGATECH_USB_TV_NTSC_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = -1,
+ .Y_Offset = 20,
+ .ModelString = "Nogatech USB-TV (NTSC) FM",
+ },
+ [PNY_USB_TV_NTSC_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = -1,
+ .Y_Offset = 20,
+ .ModelString = "PNY USB-TV (NTSC) FM",
+ },
+ [PV_PLAYTV_USB_PRO_PAL_FM] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "PixelView PlayTv-USB PRO (PAL) FM",
+ },
+ [ZT_721] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "ZTV ZT-721 2.4GHz USB A/V Receiver",
+ },
+ [HPG_WINTV_NTSC_MN] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = -1,
+ .Y_Offset = 20,
+ .ModelString = "Hauppauge WinTV USB (NTSC M/N)",
+ },
+ [HPG_WINTV_PAL_BG] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTV USB (PAL B/G)",
+ },
+ [HPG_WINTV_PAL_I] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTV USB (PAL I)",
+ },
+ [HPG_WINTV_PAL_SECAM_L] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_SECAM,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_SECAM,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTV USB (PAL/SECAM L)",
+ },
+ [HPG_WINTV_PAL_D_K] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTV USB (PAL D/K)",
+ },
+ [HPG_WINTV_NTSC_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTV USB (NTSC FM)",
+ },
+ [HPG_WINTV_PAL_BG_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTV USB (PAL B/G FM)",
+ },
+ [HPG_WINTV_PAL_I_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTV USB (PAL I FM)",
+ },
+ [HPG_WINTV_PAL_D_K_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTV USB (PAL D/K FM)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_MICROTUNE_4049FM5,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN_V2] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_MICROTUNE_4049FM5,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N) V2",
+ },
+ [HPG_WINTV_PRO_PAL] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_FM1216ME_MK3,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN_V3] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N) V3",
+ },
+ [HPG_WINTV_PRO_PAL_BG] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL B/G)",
+ },
+ [HPG_WINTV_PRO_PAL_I] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL I)",
+ },
+ [HPG_WINTV_PRO_PAL_SECAM_L] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_SECAM,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_SECAM,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM L)",
+ },
+ [HPG_WINTV_PRO_PAL_D_K] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL D/K)",
+ },
+ [HPG_WINTV_PRO_PAL_SECAM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_SECAM,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_SECAM,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)",
+ },
+ [HPG_WINTV_PRO_PAL_SECAM_V2] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_SECAM,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_SECAM,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2",
+ },
+ [HPG_WINTV_PRO_PAL_BG_V2] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_ALPS_TSBE1_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL B/G) V2",
+ },
+ [HPG_WINTV_PRO_PAL_BG_D_K] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_ALPS_TSBE1_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL B/G,D/K)",
+ },
+ [HPG_WINTV_PRO_PAL_I_D_K] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL I,D/K)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N FM)",
+ },
+ [HPG_WINTV_PRO_PAL_BG_FM] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL B/G FM)",
+ },
+ [HPG_WINTV_PRO_PAL_I_FM] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL I FM)",
+ },
+ [HPG_WINTV_PRO_PAL_D_K_FM] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL D/K FM)",
+ },
+ [HPG_WINTV_PRO_TEMIC_PAL_FM] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_MICROTUNE_4049FM5,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)",
+ },
+ [HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_MICROTUNE_4049FM5,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)",
+ },
+ [HPG_WINTV_PRO_PAL_FM] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_FM1216ME_MK3,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN_FM_V2] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2",
+ },
+ [CAMTEL_TVB330] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = 5,
+ .Y_Offset = 5,
+ .ModelString = "Camtel Technology USB TV Genie Pro FM Model TVB330",
+ },
+ [DIGITAL_VIDEO_CREATOR_I] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 0,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Digital Video Creator I",
+ },
+ [GLOBAL_VILLAGE_GV_007_NTSC] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 0,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 82,
+ .Y_Offset = 20,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Global Village GV-007 (NTSC)",
+ },
+ [DAZZLE_DVC_50_REV_1_NTSC] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 0,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)",
+ },
+ [DAZZLE_DVC_80_REV_1_PAL] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 0,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)",
+ },
+ [DAZZLE_DVC_90_REV_1_SECAM] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_SECAM,
+ .AudioChannels = 0,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)",
+ },
+ [ESKAPE_LABS_MYTV2GO] = {
+ .Interface = 0,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_FM1216ME_MK3,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Eskape Labs MyTV2Go",
+ },
+ [PINNA_PCTV_USB_PAL] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 0,
+ .Tuner = 1,
+ .TunerType = TUNER_TEMIC_4066FY5_PAL_I,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Pinnacle Studio PCTV USB (PAL)",
+ },
+ [PINNA_PCTV_USB_SECAM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_SECAM,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_SECAM,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Pinnacle Studio PCTV USB (SECAM)",
+ },
+ [PINNA_PCTV_USB_PAL_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = 128,
+ .Y_Offset = 23,
+ .ModelString = "Pinnacle Studio PCTV USB (PAL) FM",
+ },
+ [MIRO_PCTV_USB] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_PAL,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Miro PCTV USB",
+ },
+ [PINNA_PCTV_USB_NTSC_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM",
+ },
+ [PINNA_PCTV_USB_PAL_FM_V2] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_TEMIC_4009FR5_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Pinnacle Studio PCTV USB (PAL) FM V2",
+ },
+ [PINNA_PCTV_USB_NTSC_FM_V2] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_TEMIC_4039FR5_NTSC,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM V2",
+ },
+ [PINNA_PCTV_USB_PAL_FM_V3] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_TEMIC_4009FR5_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Pinnacle Studio PCTV USB (PAL) FM V3",
+ },
+ [PINNA_LINX_VD_IN_CAB_NTSC] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Pinnacle Studio Linx Video input cable (NTSC)",
+ },
+ [PINNA_LINX_VD_IN_CAB_PAL] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 2,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 0,
+ .TunerType = 0,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Pinnacle Studio Linx Video input cable (PAL)",
+ },
+ [PINNA_PCTV_BUNGEE_PAL_FM] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7113,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_PAL,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_TEMIC_4009FR5_PAL,
+ .X_Offset = 0,
+ .Y_Offset = 3,
+ .Dvi_yuv_override = 1,
+ .Dvi_yuv = 7,
+ .ModelString = "Pinnacle PCTV Bungee USB (PAL) FM",
+ },
+ [HPG_WINTV] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 0,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Hauppauge WinTv-USB",
+ },
};
+const int usbvision_device_data_size=ARRAY_SIZE(usbvision_device_data);
/* Supported Devices */
struct usb_device_id usbvision_table [] = {
- { USB_DEVICE(0xFFF0, 0xFFF0) }, /* Custom Dummy USBVision Device */
- { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */
- { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USBView II */
- { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */
- { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 */
- { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */
- { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */
- { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */
- { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge USB-Live Model 600 */
- { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */
- { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */
- { USB_DEVICE(0x0573, 0x4110) }, /* PNY USB-TV (NTSC) FM */
- { USB_DEVICE(0x0573, 0x4450) }, /* PixelView PlayTv-USB PRO (PAL) FM */
- { USB_DEVICE(0x0573, 0x4550) }, /* ZTV ZT-721 2.4GHz USB A/V Receiver */
- { USB_DEVICE(0x0573, 0x4d00) }, /* Hauppauge WinTv-USB USA */
- { USB_DEVICE(0x0573, 0x4d01) }, /* Hauppauge WinTv-USB */
- { USB_DEVICE(0x0573, 0x4d02) }, /* Hauppauge WinTv-USB UK */
- { USB_DEVICE(0x0573, 0x4d03) }, /* Hauppauge WinTv-USB France */
- { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTv-USB with FM USA radio */
- { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTv-USB (PAL) with FM radio */
- { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTv-USB UK with FM Radio */
- { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */
- { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppague WinTv USB Model 602 40201 Rev B282 */
- { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/
- { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */
- { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTv-USB II (PAL) with FM radio*/
- { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTv-USB II (PAL) Model 566 */
- { USB_DEVICE(0x0573, 0x4d23) }, /* Hauppauge WinTv-USB France 4D23*/
- { USB_DEVICE(0x0573, 0x4d25) }, /* Hauppauge WinTv-USB Model 40209 rev B234 */
- { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTv-USB Model 40209 Rev B243 */
- { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTv-USB Model 40204 Rev B281 */
- { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTv-USB Model 40204 Rev B283 */
- { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */
- { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */
- { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */
- { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTv-USB III (PAL) FM Model 573 */
- { USB_DEVICE(0x0573, 0x4d35) }, /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */
- { USB_DEVICE(0x0573, 0x4d37) }, /* Hauppauge WinTv-USB Model 40219 Rev E189 */
- { USB_DEVICE(0x0768, 0x0006) }, /* Camtel Technology USB TV Genie Pro FM Model TVB330 */
- { USB_DEVICE(0x07d0, 0x0001) }, /* Digital Video Creator I */
- { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */
- { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */
- { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */
- { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */
- { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */
- { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */
- { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */
- { USB_DEVICE(0x2304, 0x0111) }, /* Miro PCTV USB */
- { USB_DEVICE(0x2304, 0x0112) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */
- { USB_DEVICE(0x2304, 0x0210) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */
- { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */
- { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */
- { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */
- { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */
- { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */
- { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */
-
- { } /* Terminating entry */
+ { USB_DEVICE(0x0a6f, 0x0400), .driver_info=XANBOO },
+ { USB_DEVICE(0x050d, 0x0106), .driver_info=BELKIN_VIDEOBUS_II },
+ { USB_DEVICE(0x050d, 0x0207), .driver_info=BELKIN_VIDEOBUS },
+ { USB_DEVICE(0x050d, 0x0208), .driver_info=BELKIN_USB_VIDEOBUS_II },
+ { USB_DEVICE(0x0571, 0x0002), .driver_info=ECHOFX_INTERVIEW_LITE },
+ { USB_DEVICE(0x0573, 0x0003), .driver_info=USBGEAR_USBG_V1 },
+ { USB_DEVICE(0x0573, 0x0400), .driver_info=D_LINK_V100 },
+ { USB_DEVICE(0x0573, 0x2000), .driver_info=X10_USB_CAMERA },
+ { USB_DEVICE(0x0573, 0x2d00), .driver_info=HPG_WINTV_LIVE_PAL_BG },
+ { USB_DEVICE(0x0573, 0x2d01), .driver_info=HPG_WINTV_LIVE_PRO_NTSC_MN },
+ { USB_DEVICE(0x0573, 0x2101), .driver_info=ZORAN_PMD_NOGATECH },
+ { USB_DEVICE(0x0573, 0x4100), .driver_info=NOGATECH_USB_TV_NTSC_FM },
+ { USB_DEVICE(0x0573, 0x4110), .driver_info=PNY_USB_TV_NTSC_FM },
+ { USB_DEVICE(0x0573, 0x4450), .driver_info=PV_PLAYTV_USB_PRO_PAL_FM },
+ { USB_DEVICE(0x0573, 0x4550), .driver_info=ZT_721 },
+ { USB_DEVICE(0x0573, 0x4d00), .driver_info=HPG_WINTV_NTSC_MN },
+ { USB_DEVICE(0x0573, 0x4d01), .driver_info=HPG_WINTV_PAL_BG },
+ { USB_DEVICE(0x0573, 0x4d02), .driver_info=HPG_WINTV_PAL_I },
+ { USB_DEVICE(0x0573, 0x4d03), .driver_info=HPG_WINTV_PAL_SECAM_L },
+ { USB_DEVICE(0x0573, 0x4d04), .driver_info=HPG_WINTV_PAL_D_K },
+ { USB_DEVICE(0x0573, 0x4d10), .driver_info=HPG_WINTV_NTSC_FM },
+ { USB_DEVICE(0x0573, 0x4d11), .driver_info=HPG_WINTV_PAL_BG_FM },
+ { USB_DEVICE(0x0573, 0x4d12), .driver_info=HPG_WINTV_PAL_I_FM },
+ { USB_DEVICE(0x0573, 0x4d14), .driver_info=HPG_WINTV_PAL_D_K_FM },
+ { USB_DEVICE(0x0573, 0x4d2a), .driver_info=HPG_WINTV_PRO_NTSC_MN },
+ { USB_DEVICE(0x0573, 0x4d2b), .driver_info=HPG_WINTV_PRO_NTSC_MN_V2 },
+ { USB_DEVICE(0x0573, 0x4d2c), .driver_info=HPG_WINTV_PRO_PAL },
+ { USB_DEVICE(0x0573, 0x4d20), .driver_info=HPG_WINTV_PRO_NTSC_MN_V3 },
+ { USB_DEVICE(0x0573, 0x4d21), .driver_info=HPG_WINTV_PRO_PAL_BG },
+ { USB_DEVICE(0x0573, 0x4d22), .driver_info=HPG_WINTV_PRO_PAL_I },
+ { USB_DEVICE(0x0573, 0x4d23), .driver_info=HPG_WINTV_PRO_PAL_SECAM_L },
+ { USB_DEVICE(0x0573, 0x4d24), .driver_info=HPG_WINTV_PRO_PAL_D_K },
+ { USB_DEVICE(0x0573, 0x4d25), .driver_info=HPG_WINTV_PRO_PAL_SECAM },
+ { USB_DEVICE(0x0573, 0x4d26), .driver_info=HPG_WINTV_PRO_PAL_SECAM_V2 },
+ { USB_DEVICE(0x0573, 0x4d27), .driver_info=HPG_WINTV_PRO_PAL_BG_V2 },
+ { USB_DEVICE(0x0573, 0x4d28), .driver_info=HPG_WINTV_PRO_PAL_BG_D_K },
+ { USB_DEVICE(0x0573, 0x4d29), .driver_info=HPG_WINTV_PRO_PAL_I_D_K },
+ { USB_DEVICE(0x0573, 0x4d30), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM },
+ { USB_DEVICE(0x0573, 0x4d31), .driver_info=HPG_WINTV_PRO_PAL_BG_FM },
+ { USB_DEVICE(0x0573, 0x4d32), .driver_info=HPG_WINTV_PRO_PAL_I_FM },
+ { USB_DEVICE(0x0573, 0x4d34), .driver_info=HPG_WINTV_PRO_PAL_D_K_FM },
+ { USB_DEVICE(0x0573, 0x4d35), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_FM },
+ { USB_DEVICE(0x0573, 0x4d36), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_BG_FM },
+ { USB_DEVICE(0x0573, 0x4d37), .driver_info=HPG_WINTV_PRO_PAL_FM },
+ { USB_DEVICE(0x0573, 0x4d38), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM_V2 },
+ { USB_DEVICE(0x0768, 0x0006), .driver_info=CAMTEL_TVB330 },
+ { USB_DEVICE(0x07d0, 0x0001), .driver_info=DIGITAL_VIDEO_CREATOR_I },
+ { USB_DEVICE(0x07d0, 0x0002), .driver_info=GLOBAL_VILLAGE_GV_007_NTSC },
+ { USB_DEVICE(0x07d0, 0x0003), .driver_info=DAZZLE_DVC_50_REV_1_NTSC },
+ { USB_DEVICE(0x07d0, 0x0004), .driver_info=DAZZLE_DVC_80_REV_1_PAL },
+ { USB_DEVICE(0x07d0, 0x0005), .driver_info=DAZZLE_DVC_90_REV_1_SECAM },
+ { USB_DEVICE(0x07f8, 0x9104), .driver_info=ESKAPE_LABS_MYTV2GO },
+ { USB_DEVICE(0x2304, 0x010d), .driver_info=PINNA_PCTV_USB_PAL },
+ { USB_DEVICE(0x2304, 0x0109), .driver_info=PINNA_PCTV_USB_SECAM },
+ { USB_DEVICE(0x2304, 0x0110), .driver_info=PINNA_PCTV_USB_PAL_FM },
+ { USB_DEVICE(0x2304, 0x0111), .driver_info=MIRO_PCTV_USB },
+ { USB_DEVICE(0x2304, 0x0112), .driver_info=PINNA_PCTV_USB_NTSC_FM },
+ { USB_DEVICE(0x2304, 0x0210), .driver_info=PINNA_PCTV_USB_PAL_FM_V2 },
+ { USB_DEVICE(0x2304, 0x0212), .driver_info=PINNA_PCTV_USB_NTSC_FM_V2 },
+ { USB_DEVICE(0x2304, 0x0214), .driver_info=PINNA_PCTV_USB_PAL_FM_V3 },
+ { USB_DEVICE(0x2304, 0x0300), .driver_info=PINNA_LINX_VD_IN_CAB_NTSC },
+ { USB_DEVICE(0x2304, 0x0301), .driver_info=PINNA_LINX_VD_IN_CAB_PAL },
+ { USB_DEVICE(0x2304, 0x0419), .driver_info=PINNA_PCTV_BUNGEE_PAL_FM },
+ { USB_DEVICE(0x2400, 0x4200), .driver_info=HPG_WINTV },
};
MODULE_DEVICE_TABLE (usb, usbvision_table);
diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h
new file mode 100644
index 000000000000..512c5cee4145
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-cards.h
@@ -0,0 +1,66 @@
+#define XANBOO 0
+#define BELKIN_VIDEOBUS_II 1
+#define BELKIN_VIDEOBUS 2
+#define BELKIN_USB_VIDEOBUS_II 3
+#define ECHOFX_INTERVIEW_LITE 4
+#define USBGEAR_USBG_V1 5
+#define D_LINK_V100 6
+#define X10_USB_CAMERA 7
+#define HPG_WINTV_LIVE_PAL_BG 8
+#define HPG_WINTV_LIVE_PRO_NTSC_MN 9
+#define ZORAN_PMD_NOGATECH 10
+#define NOGATECH_USB_TV_NTSC_FM 11
+#define PNY_USB_TV_NTSC_FM 12
+#define PV_PLAYTV_USB_PRO_PAL_FM 13
+#define ZT_721 14
+#define HPG_WINTV_NTSC_MN 15
+#define HPG_WINTV_PAL_BG 16
+#define HPG_WINTV_PAL_I 17
+#define HPG_WINTV_PAL_SECAM_L 18
+#define HPG_WINTV_PAL_D_K 19
+#define HPG_WINTV_NTSC_FM 20
+#define HPG_WINTV_PAL_BG_FM 21
+#define HPG_WINTV_PAL_I_FM 22
+#define HPG_WINTV_PAL_D_K_FM 23
+#define HPG_WINTV_PRO_NTSC_MN 24
+#define HPG_WINTV_PRO_NTSC_MN_V2 25
+#define HPG_WINTV_PRO_PAL 26
+#define HPG_WINTV_PRO_NTSC_MN_V3 27
+#define HPG_WINTV_PRO_PAL_BG 28
+#define HPG_WINTV_PRO_PAL_I 29
+#define HPG_WINTV_PRO_PAL_SECAM_L 30
+#define HPG_WINTV_PRO_PAL_D_K 31
+#define HPG_WINTV_PRO_PAL_SECAM 32
+#define HPG_WINTV_PRO_PAL_SECAM_V2 33
+#define HPG_WINTV_PRO_PAL_BG_V2 34
+#define HPG_WINTV_PRO_PAL_BG_D_K 35
+#define HPG_WINTV_PRO_PAL_I_D_K 36
+#define HPG_WINTV_PRO_NTSC_MN_FM 37
+#define HPG_WINTV_PRO_PAL_BG_FM 38
+#define HPG_WINTV_PRO_PAL_I_FM 39
+#define HPG_WINTV_PRO_PAL_D_K_FM 40
+#define HPG_WINTV_PRO_TEMIC_PAL_FM 41
+#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 42
+#define HPG_WINTV_PRO_PAL_FM 43
+#define HPG_WINTV_PRO_NTSC_MN_FM_V2 44
+#define CAMTEL_TVB330 45
+#define DIGITAL_VIDEO_CREATOR_I 46
+#define GLOBAL_VILLAGE_GV_007_NTSC 47
+#define DAZZLE_DVC_50_REV_1_NTSC 48
+#define DAZZLE_DVC_80_REV_1_PAL 49
+#define DAZZLE_DVC_90_REV_1_SECAM 50
+#define ESKAPE_LABS_MYTV2GO 51
+#define PINNA_PCTV_USB_PAL 52
+#define PINNA_PCTV_USB_SECAM 53
+#define PINNA_PCTV_USB_PAL_FM 54
+#define MIRO_PCTV_USB 55
+#define PINNA_PCTV_USB_NTSC_FM 56
+#define PINNA_PCTV_USB_PAL_FM_V2 57
+#define PINNA_PCTV_USB_NTSC_FM_V2 58
+#define PINNA_PCTV_USB_PAL_FM_V3 59
+#define PINNA_LINX_VD_IN_CAB_NTSC 60
+#define PINNA_LINX_VD_IN_CAB_PAL 61
+#define PINNA_PCTV_BUNGEE_PAL_FM 62
+#define HPG_WINTV 63
+
+extern const int usbvision_device_data_size;
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
index f2154dc072e2..bcb551adb7e6 100644
--- a/drivers/media/video/usbvision/usbvision-core.c
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -2040,8 +2040,8 @@ int usbvision_set_input(struct usb_usbvision *usbvision)
return 0;
/* Set input format expected from decoder*/
- if (usbvision_device_data[usbvision->DevModel].Vin_Reg1 >= 0) {
- value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1 & 0xff;
+ if (usbvision_device_data[usbvision->DevModel].Vin_Reg1_override) {
+ value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1;
} else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
/* SAA7113 uses 8 bit output */
value[0] = USBVISION_8_422_SYNC;
@@ -2112,8 +2112,8 @@ int usbvision_set_input(struct usb_usbvision *usbvision)
dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */
- if(usbvision_device_data[usbvision->DevModel].Dvi_yuv >= 0){
- dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff;
+ if(usbvision_device_data[usbvision->DevModel].Dvi_yuv_override){
+ dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv;
}
else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
/* This changes as the fine sync control changes. Further investigation necessary */
@@ -2238,7 +2238,7 @@ static void call_usbvision_power_off(struct work_struct *work)
PDEBUG(DBG_FUNC, "");
down_interruptible(&usbvision->lock);
if(usbvision->user == 0) {
- usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+ usbvision_i2c_unregister(usbvision);
usbvision_power_off(usbvision);
usbvision->initialized = 0;
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
index 609e1fd9c784..025be555194f 100644
--- a/drivers/media/video/usbvision/usbvision-i2c.c
+++ b/drivers/media/video/usbvision/usbvision-i2c.c
@@ -1,8 +1,8 @@
/*
- * I2C_ALGO_USB.C
+ * usbvision_i2c.c
* i2c algorithm for USB-I2C Bridges
*
- * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ * Copyright (c) 1999-2007 Joerg Heckenbach <joerg@heckenbach-aw.de>
* Dwaine Garden <dwainegarden@rogers.com>
*
* This module is part of usbvision driver project.
@@ -39,7 +39,6 @@
#include "usbvision.h"
#define DBG_I2C 1<<0
-#define DBG_ALGO 1<<1
static int i2c_debug = 0;
@@ -49,22 +48,22 @@ MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
#define PDEBUG(level, fmt, args...) \
if (i2c_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
-static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
+static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
short len);
-static int usbvision_i2c_read(void *data, unsigned char addr, char *buf,
+static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
short len);
static inline int try_write_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)
{
- void *data;
+ struct usb_usbvision *usbvision;
int i, ret = -1;
char buf[4];
- data = i2c_get_adapdata(i2c_adap);
+ usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
buf[0] = 0x00;
for (i = 0; i <= retries; i++) {
- ret = (usbvision_i2c_write(data, addr, buf, 1));
+ ret = (usbvision_i2c_write(usbvision, addr, buf, 1));
if (ret == 1)
break; /* success! */
udelay(5);
@@ -73,8 +72,8 @@ static inline int try_write_address(struct i2c_adapter *i2c_adap,
udelay(10);
}
if (i) {
- PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr);
- PDEBUG(DBG_ALGO,"Maybe there's no device at this address");
+ PDEBUG(DBG_I2C,"Needed %d retries for address %#2x", i, addr);
+ PDEBUG(DBG_I2C,"Maybe there's no device at this address");
}
return ret;
}
@@ -82,13 +81,13 @@ static inline int try_write_address(struct i2c_adapter *i2c_adap,
static inline int try_read_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)
{
- void *data;
+ struct usb_usbvision *usbvision;
int i, ret = -1;
char buf[4];
- data = i2c_get_adapdata(i2c_adap);
+ usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
for (i = 0; i <= retries; i++) {
- ret = (usbvision_i2c_read(data, addr, buf, 1));
+ ret = (usbvision_i2c_read(usbvision, addr, buf, 1));
if (ret == 1)
break; /* success! */
udelay(5);
@@ -97,8 +96,8 @@ static inline int try_read_address(struct i2c_adapter *i2c_adap,
udelay(10);
}
if (i) {
- PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr);
- PDEBUG(DBG_ALGO,"Maybe there's no device at this address");
+ PDEBUG(DBG_I2C,"Needed %d retries for address %#2x", i, addr);
+ PDEBUG(DBG_I2C,"Maybe there's no device at this address");
}
return ret;
}
@@ -152,32 +151,32 @@ static inline int usb_find_address(struct i2c_adapter *i2c_adap,
}
static int
-usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+usbvision_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
{
struct i2c_msg *pmsg;
- void *data;
+ struct usb_usbvision *usbvision;
int i, ret;
unsigned char addr;
- data = i2c_get_adapdata(i2c_adap);
+ usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr);
if (ret != 0) {
- PDEBUG(DBG_ALGO,"got NAK from device, message #%d", i);
+ PDEBUG(DBG_I2C,"got NAK from device, message #%d", i);
return (ret < 0) ? ret : -EREMOTEIO;
}
if (pmsg->flags & I2C_M_RD) {
/* read bytes into buffer */
- ret = (usbvision_i2c_read(data, addr, pmsg->buf, pmsg->len));
+ ret = (usbvision_i2c_read(usbvision, addr, pmsg->buf, pmsg->len));
if (ret < pmsg->len) {
return (ret < 0) ? ret : -EREMOTEIO;
}
} else {
/* write bytes from buffer */
- ret = (usbvision_i2c_write(data, addr, pmsg->buf, pmsg->len));
+ ret = (usbvision_i2c_write(usbvision, addr, pmsg->buf, pmsg->len));
if (ret < pmsg->len) {
return (ret < 0) ? ret : -EREMOTEIO;
}
@@ -191,7 +190,7 @@ static int algo_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned
return 0;
}
-static u32 usb_func(struct i2c_adapter *adap)
+static u32 functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
}
@@ -199,11 +198,11 @@ static u32 usb_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */
-static struct i2c_algorithm i2c_usb_algo = {
- .master_xfer = usb_xfer,
+static struct i2c_algorithm usbvision_algo = {
+ .master_xfer = usbvision_i2c_xfer,
.smbus_xfer = NULL,
.algo_control = algo_control,
- .functionality = usb_func,
+ .functionality = functionality,
};
@@ -213,41 +212,29 @@ static struct i2c_algorithm i2c_usb_algo = {
static int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap)
{
PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]");
- PDEBUG(DBG_ALGO, "ALGO debugging is enabled [i2c]");
+ PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]");
/* register new adapter to i2c module... */
- adap->algo = &i2c_usb_algo;
+ adap->algo = &usbvision_algo;
adap->timeout = 100; /* default values, should */
adap->retries = 3; /* be replaced by defines */
i2c_add_adapter(adap);
- PDEBUG(DBG_ALGO,"i2c bus for %s registered", adap->name);
-
- return 0;
-}
-
-
-int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap)
-{
-
- i2c_del_adapter(adap);
-
- PDEBUG(DBG_ALGO,"i2c bus for %s unregistered", adap->name);
+ PDEBUG(DBG_I2C,"i2c bus for %s registered", adap->name);
return 0;
}
-
/* ----------------------------------------------------------------------- */
/* usbvision specific I2C functions */
/* ----------------------------------------------------------------------- */
static struct i2c_adapter i2c_adap_template;
static struct i2c_client i2c_client_template;
-int usbvision_init_i2c(struct usb_usbvision *usbvision)
+int usbvision_i2c_register(struct usb_usbvision *usbvision)
{
memcpy(&usbvision->i2c_adap, &i2c_adap_template,
sizeof(struct i2c_adapter));
@@ -265,7 +252,7 @@ int usbvision_init_i2c(struct usb_usbvision *usbvision)
usbvision->i2c_client.adapter = &usbvision->i2c_adap;
if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) {
- printk(KERN_ERR "usbvision_init_i2c: can't write reg\n");
+ printk(KERN_ERR "usbvision_register: can't write reg\n");
return -EBUSY;
}
@@ -287,6 +274,16 @@ int usbvision_init_i2c(struct usb_usbvision *usbvision)
return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap);
}
+int usbvision_i2c_unregister(struct usb_usbvision *usbvision)
+{
+
+ i2c_del_adapter(&(usbvision->i2c_adap));
+
+ PDEBUG(DBG_I2C,"i2c bus for %s unregistered", usbvision->i2c_adap.name);
+
+ return 0;
+}
+
void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,
void *arg)
{
@@ -300,19 +297,12 @@ static int attach_inform(struct i2c_client *client)
usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
switch (client->addr << 1) {
- case 0x43:
- case 0x4b:
- {
- struct tuner_setup tun_setup;
-
- tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
- tun_setup.type = TUNER_TDA9887;
- tun_setup.addr = client->addr;
-
- call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup);
-
+ case 0x42 << 1:
+ case 0x43 << 1:
+ case 0x4a << 1:
+ case 0x4b << 1:
+ PDEBUG(DBG_I2C,"attach_inform: tda9887 detected.");
break;
- }
case 0x42:
PDEBUG(DBG_I2C,"attach_inform: saa7114 detected.");
break;
@@ -480,7 +470,7 @@ static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision,
return len;
}
-static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
+static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
short len)
{
char *bufPtr = buf;
@@ -488,7 +478,6 @@ static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
int wrcount = 0;
int count;
int maxLen = 4;
- struct usb_usbvision *usbvision = (struct usb_usbvision *) data;
while (len > 0) {
count = (len > maxLen) ? maxLen : len;
@@ -503,14 +492,13 @@ static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
return wrcount;
}
-static int usbvision_i2c_read(void *data, unsigned char addr, char *buf,
+static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
short len)
{
char temp[4];
int retval, i;
int rdcount = 0;
int count;
- struct usb_usbvision *usbvision = (struct usb_usbvision *) data;
while (len > 0) {
count = (len > 3) ? 4 : len;
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index 6fc14557d623..216704170a4c 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -76,6 +76,7 @@
#endif
#include "usbvision.h"
+#include "usbvision-cards.h"
#define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>, Dwaine Garden <DwaineGarden@rogers.com>"
#define DRIVER_NAME "usbvision"
@@ -150,7 +151,6 @@ static int PowerOnAtOpen = 1; // Set the default device to power on at startu
static int video_nr = -1; // Sequential Number of Video Device
static int radio_nr = -1; // Sequential Number of Radio Device
static int vbi_nr = -1; // Sequential Number of VBI Device
-static char *CustomDevice=NULL; // Set as nothing....
// Grab parameters for the device driver
@@ -161,7 +161,6 @@ module_param(PowerOnAtOpen, int, 0444);
module_param(video_nr, int, 0444);
module_param(radio_nr, int, 0444);
module_param(vbi_nr, int, 0444);
-module_param(CustomDevice, charp, 0444);
#else // Old Style
MODULE_PARAM(isocMode, "i");
MODULE_PARM(video_debug, "i"); // Grab the Debug Mode of the device driver
@@ -171,7 +170,6 @@ MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White ou
MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...)
MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...)
MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...)
-MODULE_PARM(CustomDevice, "s"); // .... CustomDevice
#endif
MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)");
@@ -180,7 +178,6 @@ MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device
MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)");
MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)");
-MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device. Default: null");
// Misc stuff
@@ -409,7 +406,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file)
down(&usbvision->lock);
if (usbvision->power == 0) {
usbvision_power_on(usbvision);
- usbvision_init_i2c(usbvision);
+ usbvision_i2c_register(usbvision);
}
/* Send init sequence only once, it's large! */
@@ -431,7 +428,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file)
}
else {
if (PowerOnAtOpen) {
- usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+ usbvision_i2c_unregister(usbvision);
usbvision_power_off(usbvision);
usbvision->initialized = 0;
}
@@ -1239,7 +1236,7 @@ static int usbvision_radio_open(struct inode *inode, struct file *file)
usbvision_reset_powerOffTimer(usbvision);
if (usbvision->power == 0) {
usbvision_power_on(usbvision);
- usbvision_init_i2c(usbvision);
+ usbvision_i2c_register(usbvision);
}
}
@@ -1261,7 +1258,7 @@ static int usbvision_radio_open(struct inode *inode, struct file *file)
if (errCode) {
if (PowerOnAtOpen) {
- usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+ usbvision_i2c_unregister(usbvision);
usbvision_power_off(usbvision);
usbvision->initialized = 0;
}
@@ -1744,8 +1741,8 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision)
model = usbvision->DevModel;
usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24;
- if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) {
- usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff;
+ if (usbvision_device_data[usbvision->DevModel].Vin_Reg2_override) {
+ usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2;
} else {
usbvision->Vin_Reg2_Preset = 0;
}
@@ -1764,7 +1761,7 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision)
usbvision_audio_off(usbvision); //first switch off audio
if (!PowerOnAtOpen) {
usbvision_power_on(usbvision); //and then power up the noisy tuner
- usbvision_init_i2c(usbvision);
+ usbvision_i2c_register(usbvision);
}
}
@@ -1775,7 +1772,8 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision)
* if it looks like USBVISION video device
*
*/
-static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+static int __devinit usbvision_probe(struct usb_interface *intf,
+ const struct usb_device_id *devid)
{
struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf));
struct usb_interface *uif;
@@ -1786,25 +1784,17 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us
int model,i;
PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
- dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct, ifnum);
- /* Is it an USBVISION video dev? */
- model = 0;
- for(model = 0; usbvision_device_data[model].idVendor; model++) {
- if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) {
- continue;
- }
- if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) {
- continue;
- }
-
- printk(KERN_INFO "%s: %s found\n", __FUNCTION__, usbvision_device_data[model].ModelString);
- break;
+ model = devid->driver_info;
+ if ( (model<0) || (model>=usbvision_device_data_size) ) {
+ PDEBUG(DBG_PROBE, "model out of bounds %d",model);
+ return -ENODEV;
}
+ printk(KERN_INFO "%s: %s found\n", __FUNCTION__,
+ usbvision_device_data[model].ModelString);
- if (usbvision_device_data[model].idVendor == 0) {
- return -ENODEV; //no matching device
- }
if (usbvision_device_data[model].Interface >= 0) {
interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0];
}
@@ -1822,16 +1812,15 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us
return -ENODEV;
}
- usb_get_dev(dev);
-
if ((usbvision = usbvision_alloc(dev)) == NULL) {
err("%s: couldn't allocate USBVision struct", __FUNCTION__);
return -ENOMEM;
}
+
if (dev->descriptor.bNumConfigurations > 1) {
usbvision->bridgeType = BRIDGE_NT1004;
}
- else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") {
+ else if (model == DAZZLE_DVC_90_REV_1_SECAM) {
usbvision->bridgeType = BRIDGE_NT1005;
}
else {
@@ -1920,7 +1909,7 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf)
usbvision_stop_isoc(usbvision);
if (usbvision->power) {
- usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+ usbvision_i2c_unregister(usbvision);
usbvision_power_off(usbvision);
}
usbvision->remove_pending = 1; // Now all ISO data will be ignored
@@ -1951,124 +1940,6 @@ static struct usb_driver usbvision_driver = {
};
/*
- * customdevice_process()
- *
- * This procedure preprocesses CustomDevice parameter if any
- *
- */
-static void customdevice_process(void)
-{
- usbvision_device_data[0]=usbvision_device_data[1];
- usbvision_table[0]=usbvision_table[1];
-
- if(CustomDevice)
- {
- char *parse=CustomDevice;
-
- PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice);
-
- /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1"
- usbvision_device_data[0].idVendor;
- usbvision_device_data[0].idProduct;
- usbvision_device_data[0].Interface;
- usbvision_device_data[0].Codec;
- usbvision_device_data[0].VideoChannels;
- usbvision_device_data[0].VideoNorm;
- usbvision_device_data[0].AudioChannels;
- usbvision_device_data[0].Radio;
- usbvision_device_data[0].Tuner;
- usbvision_device_data[0].TunerType;
- usbvision_device_data[0].Vin_Reg1;
- usbvision_device_data[0].Vin_Reg2;
- usbvision_device_data[0].X_Offset;
- usbvision_device_data[0].Y_Offset;
- usbvision_device_data[0].Dvi_yuv;
- usbvision_device_data[0].ModelString;
- */
-
- rmspace(parse);
- usbvision_device_data[0].ModelString="USBVISION Custom Device";
-
- parse+=2;
- sscanf(parse,"%x",&usbvision_device_data[0].idVendor);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor);
- parse+=2;
- sscanf(parse,"%x",&usbvision_device_data[0].idProduct);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct);
- sscanf(parse,"%d",&usbvision_device_data[0].Interface);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface);
- sscanf(parse,"%d",&usbvision_device_data[0].Codec);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec);
- sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels);
-
- switch(*parse)
- {
- case 'P':
- PDEBUG(DBG_PROBE, "VideoNorm=PAL");
- usbvision_device_data[0].VideoNorm=V4L2_STD_PAL;
- break;
-
- case 'S':
- PDEBUG(DBG_PROBE, "VideoNorm=SECAM");
- usbvision_device_data[0].VideoNorm=V4L2_STD_SECAM;
- break;
-
- case 'N':
- PDEBUG(DBG_PROBE, "VideoNorm=NTSC");
- usbvision_device_data[0].VideoNorm=V4L2_STD_NTSC;
- break;
-
- default:
- PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)");
- usbvision_device_data[0].VideoNorm=V4L2_STD_PAL;
- break;
- }
- goto2next(parse);
-
- sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels);
- sscanf(parse,"%d",&usbvision_device_data[0].Radio);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio);
- sscanf(parse,"%d",&usbvision_device_data[0].Tuner);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner);
- sscanf(parse,"%d",&usbvision_device_data[0].TunerType);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType);
- sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1);
- sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2);
- sscanf(parse,"%d",&usbvision_device_data[0].X_Offset);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset);
- sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset);
- goto2next(parse);
- PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset);
- sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv);
- PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv);
-
- //add to usbvision_table also
- usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE;
- usbvision_table[0].idVendor=usbvision_device_data[0].idVendor;
- usbvision_table[0].idProduct=usbvision_device_data[0].idProduct;
-
- }
-}
-
-
-
-/*
* usbvision_init()
*
* This code is run to initialize the driver.
@@ -2092,8 +1963,6 @@ static int __init usbvision_init(void)
usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P
}
- customdevice_process();
-
errCode = usb_register(&usbvision_driver);
if (errCode == 0) {
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
index ad6afd3e42a4..bd6f6422ed54 100644
--- a/drivers/media/video/usbvision/usbvision.h
+++ b/drivers/media/video/usbvision/usbvision.h
@@ -342,23 +342,24 @@ struct usbvision_frame {
#define BRIDGE_NT1005 1005
struct usbvision_device_data_st {
- int idVendor;
- int idProduct;
- int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */
- int Codec;
- int VideoChannels;
__u64 VideoNorm;
- int AudioChannels;
- int Radio;
- int vbi;
- int Tuner;
- int TunerType;
- int Vin_Reg1;
- int Vin_Reg2;
- int X_Offset;
- int Y_Offset;
- int Dvi_yuv;
- char *ModelString;
+ const char *ModelString;
+ int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */
+ __u16 Codec;
+ unsigned VideoChannels:3;
+ unsigned AudioChannels:2;
+ unsigned Radio:1;
+ unsigned vbi:1;
+ unsigned Tuner:1;
+ unsigned Vin_Reg1_override:1; /* Override default value with */
+ unsigned Vin_Reg2_override:1; /* Vin_Reg1, Vin_Reg2, etc. */
+ unsigned Dvi_yuv_override:1;
+ __u8 Vin_Reg1;
+ __u8 Vin_Reg2;
+ __u8 Dvi_yuv;
+ __u8 TunerType;
+ __s16 X_Offset;
+ __s16 Y_Offset;
};
/* Declared on usbvision-cards.c */
@@ -481,13 +482,11 @@ struct usb_usbvision {
/* i2c-algo-usb declaration */
/* --------------------------------------------------------------- */
-int usbvision_i2c_usb_del_bus(struct i2c_adapter *);
-
-
/* ----------------------------------------------------------------------- */
/* usbvision specific I2C functions */
/* ----------------------------------------------------------------------- */
-int usbvision_init_i2c(struct usb_usbvision *usbvision);
+int usbvision_i2c_register(struct usb_usbvision *usbvision);
+int usbvision_i2c_unregister(struct usb_usbvision *usbvision);
void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg);
/* defined in usbvision-core.c */
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 54747606eae1..49f1df74aa21 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -60,6 +60,7 @@
#include <linux/video_decoder.h>
#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
@@ -260,6 +261,8 @@ char *v4l2_field_names[] = {
[V4L2_FIELD_SEQ_TB] = "seq-tb",
[V4L2_FIELD_SEQ_BT] = "seq-bt",
[V4L2_FIELD_ALTERNATE] = "alternate",
+ [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
+ [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
};
char *v4l2_type_names[] = {
@@ -269,7 +272,8 @@ char *v4l2_type_names[] = {
[V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap",
[V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out",
[V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
- [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "slicec-vbi-out",
+ [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out",
+ [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over",
};
@@ -380,6 +384,8 @@ static const char *v4l2_ioctls[] = {
[_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER",
[_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER",
+
+ [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT",
#endif
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -410,14 +416,16 @@ static const char *v4l2_int_ioctls[] = {
[_IOC_NR(VIDIOC_INT_DECODE_VBI_LINE)] = "VIDIOC_INT_DECODE_VBI_LINE",
[_IOC_NR(VIDIOC_INT_S_VBI_DATA)] = "VIDIOC_INT_S_VBI_DATA",
[_IOC_NR(VIDIOC_INT_G_VBI_DATA)] = "VIDIOC_INT_G_VBI_DATA",
- [_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)] = "VIDIOC_INT_G_CHIP_IDENT",
[_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ",
[_IOC_NR(VIDIOC_INT_S_STANDBY)] = "VIDIOC_INT_S_STANDBY",
[_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)] = "VIDIOC_INT_S_AUDIO_ROUTING",
[_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)] = "VIDIOC_INT_G_AUDIO_ROUTING",
[_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)] = "VIDIOC_INT_S_VIDEO_ROUTING",
[_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING",
- [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ"
+ [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ",
+ [_IOC_NR(VIDIOC_INT_INIT)] = "VIDIOC_INT_INIT",
+ [_IOC_NR(VIDIOC_INT_G_STD_OUTPUT)] = "VIDIOC_INT_G_STD_OUTPUT",
+ [_IOC_NR(VIDIOC_INT_S_STD_OUTPUT)] = "VIDIOC_INT_S_STD_OUTPUT",
};
#define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls)
@@ -680,6 +688,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break;
case V4L2_CID_MPEG_AUDIO_EMPHASIS: name = "Audio Emphasis"; break;
case V4L2_CID_MPEG_AUDIO_CRC: name = "Audio CRC"; break;
+ case V4L2_CID_MPEG_AUDIO_MUTE: name = "Audio Mute"; break;
case V4L2_CID_MPEG_VIDEO_ENCODING: name = "Video Encoding"; break;
case V4L2_CID_MPEG_VIDEO_ASPECT: name = "Video Aspect"; break;
case V4L2_CID_MPEG_VIDEO_B_FRAMES: name = "Video B Frames"; break;
@@ -690,6 +699,8 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
case V4L2_CID_MPEG_VIDEO_BITRATE: name = "Video Bitrate"; break;
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: name = "Video Peak Bitrate"; break;
case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: name = "Video Temporal Decimation"; break;
+ case V4L2_CID_MPEG_VIDEO_MUTE: name = "Video Mute"; break;
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: name = "Video Mute YUV"; break;
case V4L2_CID_MPEG_STREAM_TYPE: name = "Stream Type"; break;
case V4L2_CID_MPEG_STREAM_PID_PMT: name = "Stream PMT Program ID"; break;
case V4L2_CID_MPEG_STREAM_PID_AUDIO: name = "Stream Audio Program ID"; break;
@@ -705,6 +716,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
switch (qctrl->id) {
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_MPEG_AUDIO_MUTE:
case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
case V4L2_CID_MPEG_VIDEO_PULLDOWN:
qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
@@ -838,6 +850,8 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl)
V4L2_MPEG_AUDIO_CRC_NONE,
V4L2_MPEG_AUDIO_CRC_CRC16, 1,
V4L2_MPEG_AUDIO_CRC_NONE);
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
case V4L2_CID_MPEG_VIDEO_ENCODING:
return v4l2_ctrl_query_fill(qctrl,
V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
@@ -867,6 +881,10 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl)
return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */
+ return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080);
case V4L2_CID_MPEG_STREAM_TYPE:
return v4l2_ctrl_query_fill(qctrl,
V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
@@ -965,6 +983,22 @@ int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 match_type, u32 match_c
}
}
+int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip,
+ u32 ident, u32 revision)
+{
+ if (!v4l2_chip_match_i2c_client(c, chip->match_type, chip->match_chip))
+ return 0;
+ if (chip->ident == V4L2_IDENT_NONE) {
+ chip->ident = ident;
+ chip->revision = revision;
+ }
+ else {
+ chip->ident = V4L2_IDENT_AMBIGUOUS;
+ chip->revision = 0;
+ }
+ return 0;
+}
+
int v4l2_chip_match_host(u32 match_type, u32 match_chip)
{
switch (match_type) {
@@ -999,6 +1033,7 @@ EXPORT_SYMBOL(v4l2_ctrl_query_fill);
EXPORT_SYMBOL(v4l2_ctrl_query_fill_std);
EXPORT_SYMBOL(v4l2_chip_match_i2c_client);
+EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
EXPORT_SYMBOL(v4l2_chip_match_host);
/*
diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c
index 290e64135650..f2bbd7a4d562 100644
--- a/drivers/media/video/videocodec.c
+++ b/drivers/media/video/videocodec.c
@@ -348,6 +348,9 @@ videocodec_build_table (void)
kfree(videocodec_buf);
videocodec_buf = kmalloc(size, GFP_KERNEL);
+ if (!videocodec_buf)
+ return 0;
+
i = 0;
i += scnprintf(videocodec_buf + i, size - 1,
"<S>lave or attached <M>aster name type flags magic ");
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index 011938fb7e0e..80ac5f86d9e5 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -318,6 +318,7 @@ static char *v4l2_type_names_FIXME[] = {
[V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out",
[V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out",
[V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-capture",
+ [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over",
[V4L2_BUF_TYPE_PRIVATE] = "private",
};
@@ -330,6 +331,8 @@ static char *v4l2_field_names_FIXME[] = {
[V4L2_FIELD_SEQ_TB] = "seq-tb",
[V4L2_FIELD_SEQ_BT] = "seq-bt",
[V4L2_FIELD_ALTERNATE] = "alternate",
+ [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
+ [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
};
#define prt_names(a,arr) (((a)>=0)&&((a)<ARRAY_SIZE(arr)))?arr[a]:"unknown"
@@ -411,6 +414,10 @@ static int check_fmt (struct video_device *vfd, enum v4l2_buf_type type)
if (vfd->vidioc_try_fmt_vbi_output)
return (0);
break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (vfd->vidioc_try_fmt_output_overlay)
+ return (0);
+ break;
case V4L2_BUF_TYPE_PRIVATE:
if (vfd->vidioc_try_fmt_type_private)
return (0);
@@ -525,6 +532,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret=vfd->vidioc_enum_fmt_vbi_output(file,
fh, f);
break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (vfd->vidioc_enum_fmt_output_overlay)
+ ret=vfd->vidioc_enum_fmt_output_overlay(file, fh, f);
+ break;
case V4L2_BUF_TYPE_PRIVATE:
if (vfd->vidioc_enum_fmt_type_private)
ret=vfd->vidioc_enum_fmt_type_private(file,
@@ -582,6 +593,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret=vfd->vidioc_g_fmt_video_output(file,
fh, f);
break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (vfd->vidioc_g_fmt_output_overlay)
+ ret=vfd->vidioc_g_fmt_output_overlay(file, fh, f);
+ break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
if (vfd->vidioc_g_fmt_vbi_output)
ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f);
@@ -630,6 +645,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret=vfd->vidioc_s_fmt_video_output(file,
fh, f);
break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (vfd->vidioc_s_fmt_output_overlay)
+ ret=vfd->vidioc_s_fmt_output_overlay(file, fh, f);
+ break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
if (vfd->vidioc_s_fmt_vbi_output)
ret=vfd->vidioc_s_fmt_vbi_output(file,
@@ -680,6 +699,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret=vfd->vidioc_try_fmt_video_output(file,
fh, f);
break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (vfd->vidioc_try_fmt_output_overlay)
+ ret=vfd->vidioc_try_fmt_output_overlay(file, fh, f);
+ break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
if (vfd->vidioc_try_fmt_vbi_output)
ret=vfd->vidioc_try_fmt_vbi_output(file,
@@ -1381,6 +1404,11 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_G_PARM:
{
struct v4l2_streamparm *p=arg;
+ __u32 type=p->type;
+
+ memset(p,0,sizeof(*p));
+ p->type=type;
+
if (vfd->vidioc_g_parm) {
ret=vfd->vidioc_g_parm(file, fh, p);
} else {
@@ -1392,8 +1420,6 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
v4l2_video_std_construct(&s, vfd->current_norm,
v4l2_norm_to_name(vfd->current_norm));
- memset(p,0,sizeof(*p));
-
p->parm.capture.timeperframe = s.frameperiod;
ret=0;
}
@@ -1509,6 +1535,16 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
break;
}
#endif
+ case VIDIOC_G_CHIP_IDENT:
+ {
+ struct v4l2_chip_ident *p=arg;
+ if (!vfd->vidioc_g_chip_ident)
+ break;
+ ret=vfd->vidioc_g_chip_ident(file, fh, p);
+ if (!ret)
+ dbgarg (cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision);
+ break;
+ }
} /* switch */
if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index a9b59c35cd67..8f6741a28a47 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -29,6 +29,7 @@
#include <linux/i2c-id.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("wm8739 driver");
MODULE_AUTHOR("T. Adachi, Hans Verkuil");
@@ -236,6 +237,9 @@ static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg
return -EINVAL;
}
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8739, 0);
+
case VIDIOC_LOG_STATUS:
v4l_info(client, "Frequency: %u Hz\n", state->clock_freq);
v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f,
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index d81a88bbe43d..4df5d30d4d09 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -33,6 +33,7 @@
#include <linux/i2c-id.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("wm8775 driver");
MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
@@ -124,6 +125,9 @@ static int wm8775_command(struct i2c_client *client, unsigned int cmd,
wm8775_write(client, R21, 0x100 + state->input);
break;
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8775, 0);
+
case VIDIOC_LOG_STATUS:
v4l_info(client, "Input: %d%s\n", state->input,
state->muted ? " (muted)" : "");
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
new file mode 100644
index 000000000000..b5d3364c94c7
--- /dev/null
+++ b/drivers/media/video/zr364xx.c
@@ -0,0 +1,929 @@
+/*
+ * Zoran 364xx based USB webcam module version 0.72
+ *
+ * Allows you to use your USB webcam with V4L2 applications
+ * This is still in heavy developpement !
+ *
+ * Copyright (C) 2004 Antoine Jacquet <royale@zerezo.com>
+ * http://royale.zerezo.com/zr364xx/
+ *
+ * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers
+ * V4L2 version inspired by meye.c driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/highmem.h>
+#include <media/v4l2-common.h>
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.72"
+#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
+#define DRIVER_DESC "Zoran 364xx"
+
+
+/* Camera */
+#define FRAMES 2
+#define MAX_FRAME_SIZE 100000
+#define BUFFER_SIZE 0x1000
+#define CTRL_TIMEOUT 500
+
+
+/* Debug macro */
+#define DBG(x...) if (debug) info(x)
+
+
+/* Init methods, need to find nicer names for these
+ * the exact names of the chipsets would be the best if someone finds it */
+#define METHOD0 0
+#define METHOD1 1
+#define METHOD2 2
+
+
+/* Module parameters */
+static int debug = 0;
+static int mode = 0;
+
+
+/* Module parameters interface */
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level");
+module_param(mode, int, 0644);
+MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480");
+
+
+/* Devices supported by this driver
+ * .driver_info contains the init method used by the camera */
+static struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 },
+ {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 },
+ {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 },
+ {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 },
+ {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 },
+ {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 },
+ {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 },
+ {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+
+/* Camera stuff */
+struct zr364xx_camera {
+ struct usb_device *udev; /* save off the usb device pointer */
+ struct usb_interface *interface;/* the interface for this device */
+ struct video_device *vdev; /* v4l video device */
+ u8 *framebuf;
+ int nb;
+ unsigned char *buffer;
+ int skip;
+ int brightness;
+ int width;
+ int height;
+ int method;
+ struct mutex lock;
+};
+
+
+/* function used to send initialisation commands to the camera */
+static int send_control_msg(struct usb_device *udev, u8 request, u16 value,
+ u16 index, unsigned char *cp, u16 size)
+{
+ int status;
+
+ unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL);
+ if (!transfer_buffer) {
+ info("kmalloc(%d) failed", size);
+ return -ENOMEM;
+ }
+
+ memcpy(transfer_buffer, cp, size);
+
+ status = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ request,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, value, index,
+ transfer_buffer, size, CTRL_TIMEOUT);
+
+ kfree(transfer_buffer);
+
+ if (status < 0)
+ info("Failed sending control message, error %d.", status);
+
+ return status;
+}
+
+
+/* Control messages sent to the camera to initialize it
+ * and launch the capture */
+typedef struct {
+ unsigned int value;
+ unsigned int size;
+ unsigned char *bytes;
+} message;
+
+/* method 0 */
+static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 };
+static unsigned char m0d3[] = { 0, 0 };
+static message m0[] = {
+ {0x1f30, 0, NULL},
+ {0xd000, 0, NULL},
+ {0x3370, sizeof(m0d1), m0d1},
+ {0x2000, 0, NULL},
+ {0x2f0f, 0, NULL},
+ {0x2610, sizeof(m0d2), m0d2},
+ {0xe107, 0, NULL},
+ {0x2502, 0, NULL},
+ {0x1f70, 0, NULL},
+ {0xd000, 0, NULL},
+ {0x9a01, sizeof(m0d3), m0d3},
+ {-1, -1, NULL}
+};
+
+/* method 1 */
+static unsigned char m1d1[] = { 0xff, 0xff };
+static unsigned char m1d2[] = { 0x00, 0x00 };
+static message m1[] = {
+ {0x1f30, 0, NULL},
+ {0xd000, 0, NULL},
+ {0xf000, 0, NULL},
+ {0x2000, 0, NULL},
+ {0x2f0f, 0, NULL},
+ {0x2650, 0, NULL},
+ {0xe107, 0, NULL},
+ {0x2502, sizeof(m1d1), m1d1},
+ {0x1f70, 0, NULL},
+ {0xd000, 0, NULL},
+ {0xd000, 0, NULL},
+ {0xd000, 0, NULL},
+ {0x9a01, sizeof(m1d2), m1d2},
+ {-1, -1, NULL}
+};
+
+/* method 2 */
+static unsigned char m2d1[] = { 0xff, 0xff };
+static message m2[] = {
+ {0x1f30, 0, NULL},
+ {0xf000, 0, NULL},
+ {0x2000, 0, NULL},
+ {0x2f0f, 0, NULL},
+ {0x2650, 0, NULL},
+ {0xe107, 0, NULL},
+ {0x2502, sizeof(m2d1), m2d1},
+ {0x1f70, 0, NULL},
+ {-1, -1, NULL}
+};
+
+/* init table */
+static message *init[3] = { m0, m1, m2 };
+
+
+/* JPEG static data in header (Huffman table, etc) */
+static unsigned char header1[] = {
+ 0xFF, 0xD8,
+ /*
+ 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F',
+ 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88,
+ */
+ 0xFF, 0xDB, 0x00, 0x84
+};
+static unsigned char header2[] = {
+ 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
+ 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01,
+ 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+ 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+ 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33,
+ 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94,
+ 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
+ 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+ 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA,
+ 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
+ 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3,
+ 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F,
+ 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5,
+ 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+ 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11,
+ 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1,
+ 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16,
+ 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84,
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA,
+ 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3,
+ 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5,
+ 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01,
+ 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01,
+ 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11,
+ 0x00, 0x3F, 0x00
+};
+static unsigned char header3;
+
+
+
+/********************/
+/* V4L2 integration */
+/********************/
+
+/* this function reads a full JPEG picture synchronously
+ * TODO: do it asynchronously... */
+static int read_frame(struct zr364xx_camera *cam, int framenum)
+{
+ int i, n, temp, head, size, actual_length;
+ unsigned char *ptr = NULL, *jpeg;
+
+ redo:
+ /* hardware brightness */
+ n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0);
+ temp = (0x60 << 8) + 127 - cam->brightness;
+ n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0);
+
+ /* during the first loop we are going to insert JPEG header */
+ head = 0;
+ /* this is the place in memory where we are going to build
+ * the JPEG image */
+ jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE;
+ /* read data... */
+ do {
+ n = usb_bulk_msg(cam->udev,
+ usb_rcvbulkpipe(cam->udev, 0x81),
+ cam->buffer, BUFFER_SIZE, &actual_length,
+ CTRL_TIMEOUT);
+ DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]);
+ DBG("bulk : n=%d size=%d", n, actual_length);
+ if (n < 0) {
+ info("error reading bulk msg");
+ return 0;
+ }
+ if (actual_length < 0 || actual_length > BUFFER_SIZE) {
+ info("wrong number of bytes");
+ return 0;
+ }
+
+ /* swap bytes if camera needs it */
+ if (cam->method == METHOD0) {
+ u16 *buf = (u16*)cam->buffer;
+ for (i = 0; i < BUFFER_SIZE/2; i++)
+ swab16s(buf + i);
+ }
+
+ /* write the JPEG header */
+ if (!head) {
+ DBG("jpeg header");
+ ptr = jpeg;
+ memcpy(ptr, header1, sizeof(header1));
+ ptr += sizeof(header1);
+ header3 = 0;
+ memcpy(ptr, &header3, 1);
+ ptr++;
+ memcpy(ptr, cam->buffer, 64);
+ ptr += 64;
+ header3 = 1;
+ memcpy(ptr, &header3, 1);
+ ptr++;
+ memcpy(ptr, cam->buffer + 64, 64);
+ ptr += 64;
+ memcpy(ptr, header2, sizeof(header2));
+ ptr += sizeof(header2);
+ memcpy(ptr, cam->buffer + 128,
+ actual_length - 128);
+ ptr += actual_length - 128;
+ head = 1;
+ DBG("header : %d %d %d %d %d %d %d %d %d",
+ cam->buffer[0], cam->buffer[1], cam->buffer[2],
+ cam->buffer[3], cam->buffer[4], cam->buffer[5],
+ cam->buffer[6], cam->buffer[7], cam->buffer[8]);
+ } else {
+ memcpy(ptr, cam->buffer, actual_length);
+ ptr += actual_length;
+ }
+ }
+ /* ... until there is no more */
+ while (actual_length == BUFFER_SIZE);
+
+ /* we skip the 2 first frames which are usually buggy */
+ if (cam->skip) {
+ cam->skip--;
+ goto redo;
+ }
+
+ /* go back to find the JPEG EOI marker */
+ size = ptr - jpeg;
+ ptr -= 2;
+ while (ptr > jpeg) {
+ if (*ptr == 0xFF && *(ptr + 1) == 0xD9
+ && *(ptr + 2) == 0xFF)
+ break;
+ ptr--;
+ }
+ if (ptr == jpeg)
+ DBG("No EOI marker");
+
+ /* Sometimes there is junk data in the middle of the picture,
+ * we want to skip this bogus frames */
+ while (ptr > jpeg) {
+ if (*ptr == 0xFF && *(ptr + 1) == 0xFF
+ && *(ptr + 2) == 0xFF)
+ break;
+ ptr--;
+ }
+ if (ptr != jpeg) {
+ DBG("Bogus frame ? %d", cam->nb);
+ goto redo;
+ }
+
+ DBG("jpeg : %d %d %d %d %d %d %d %d",
+ jpeg[0], jpeg[1], jpeg[2], jpeg[3],
+ jpeg[4], jpeg[5], jpeg[6], jpeg[7]);
+
+ return size;
+}
+
+
+static ssize_t zr364xx_read(struct file *file, char *buf, size_t cnt,
+ loff_t * ppos)
+{
+ unsigned long count = cnt;
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+
+ DBG("zr364xx_read: read %d bytes.", (int) count);
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ if (!buf)
+ return -EINVAL;
+
+ if (!count)
+ return -EINVAL;
+
+ /* NoMan Sux ! */
+ count = read_frame(cam, 0);
+
+ if (copy_to_user(buf, cam->framebuf, count))
+ return -EFAULT;
+
+ return count;
+}
+
+
+static int zr364xx_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ memset(cap, 0, sizeof(*cap));
+ strcpy(cap->driver, DRIVER_DESC);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int zr364xx_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ memset(i, 0, sizeof(*i));
+ i->index = 0;
+ strcpy(i->name, DRIVER_DESC " Camera");
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ return 0;
+}
+
+static int zr364xx_vidioc_g_input(struct file *file, void *priv,
+ unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int zr364xx_vidioc_s_input(struct file *file, void *priv,
+ unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Brightness");
+ c->minimum = 0;
+ c->maximum = 127;
+ c->step = 1;
+ c->default_value = cam->brightness;
+ c->flags = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cam->brightness = c->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = cam->brightness;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int zr364xx_vidioc_enum_fmt_cap(struct file *file,
+ void *priv, struct v4l2_fmtdesc *f)
+{
+ if (f->index > 0)
+ return -EINVAL;
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ memset(f, 0, sizeof(*f));
+ f->index = 0;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strcpy(f->description, "JPEG");
+ f->pixelformat = V4L2_PIX_FMT_JPEG;
+ return 0;
+}
+
+static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+ if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+ f->fmt.pix.field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = cam->width;
+ f->fmt.pix.height = cam->height;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = cam->width;
+ f->fmt.pix.height = cam->height;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+ if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+ f->fmt.pix.field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = cam->width;
+ f->fmt.pix.height = cam->height;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ DBG("ok!");
+ return 0;
+}
+
+static int zr364xx_vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ return 0;
+}
+
+static int zr364xx_vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ return 0;
+}
+
+
+/* open the camera */
+static int zr364xx_open(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam = video_get_drvdata(vdev);
+ struct usb_device *udev = cam->udev;
+ int i, err;
+
+ DBG("zr364xx_open");
+
+ cam->skip = 2;
+
+ err = video_exclusive_open(inode, file);
+ if (err < 0)
+ return err;
+
+ if (!cam->framebuf) {
+ cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES);
+ if (!cam->framebuf) {
+ info("vmalloc_32 failed!");
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&cam->lock);
+ for (i = 0; init[cam->method][i].size != -1; i++) {
+ err =
+ send_control_msg(udev, 1, init[cam->method][i].value,
+ 0, init[cam->method][i].bytes,
+ init[cam->method][i].size);
+ if (err < 0) {
+ info("error during open sequence: %d", i);
+ mutex_unlock(&cam->lock);
+ return err;
+ }
+ }
+
+ file->private_data = vdev;
+
+ /* Added some delay here, since opening/closing the camera quickly,
+ * like Ekiga does during its startup, can crash the webcam
+ */
+ mdelay(100);
+
+ mutex_unlock(&cam->lock);
+ return 0;
+}
+
+
+/* release the camera */
+static int zr364xx_release(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+ struct usb_device *udev;
+ int i, err;
+
+ DBG("zr364xx_release");
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ udev = cam->udev;
+
+ mutex_lock(&cam->lock);
+ for (i = 0; i < 2; i++) {
+ err =
+ send_control_msg(udev, 1, init[cam->method][i].value,
+ 0, init[i][cam->method].bytes,
+ init[cam->method][i].size);
+ if (err < 0) {
+ info("error during release sequence");
+ mutex_unlock(&cam->lock);
+ return err;
+ }
+ }
+
+ file->private_data = NULL;
+ video_exclusive_release(inode, file);
+
+ /* Added some delay here, since opening/closing the camera quickly,
+ * like Ekiga does during its startup, can crash the webcam
+ */
+ mdelay(100);
+
+ mutex_unlock(&cam->lock);
+ return 0;
+}
+
+
+static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ void *pos;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam;
+
+ DBG("zr364xx_mmap: %ld\n", size);
+
+ if (vdev == NULL)
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+ pos = cam->framebuf;
+ while (size > 0) {
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos)))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ return 0;
+}
+
+
+static struct file_operations zr364xx_fops = {
+ .owner = THIS_MODULE,
+ .open = zr364xx_open,
+ .release = zr364xx_release,
+ .read = zr364xx_read,
+ .mmap = zr364xx_mmap,
+ .ioctl = video_ioctl2,
+ .llseek = no_llseek,
+};
+
+static struct video_device zr364xx_template = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_DESC,
+ .type = VID_TYPE_CAPTURE,
+ .fops = &zr364xx_fops,
+ .release = video_device_release,
+ .minor = -1,
+
+ .vidioc_querycap = zr364xx_vidioc_querycap,
+ .vidioc_enum_fmt_cap = zr364xx_vidioc_enum_fmt_cap,
+ .vidioc_try_fmt_cap = zr364xx_vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = zr364xx_vidioc_s_fmt_cap,
+ .vidioc_g_fmt_cap = zr364xx_vidioc_g_fmt_cap,
+ .vidioc_enum_input = zr364xx_vidioc_enum_input,
+ .vidioc_g_input = zr364xx_vidioc_g_input,
+ .vidioc_s_input = zr364xx_vidioc_s_input,
+ .vidioc_streamon = zr364xx_vidioc_streamon,
+ .vidioc_streamoff = zr364xx_vidioc_streamoff,
+ .vidioc_queryctrl = zr364xx_vidioc_queryctrl,
+ .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl,
+ .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl,
+};
+
+
+
+/*******************/
+/* USB integration */
+/*******************/
+
+static int zr364xx_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct zr364xx_camera *cam = NULL;
+
+ DBG("probing...");
+
+ info(DRIVER_DESC " compatible webcam plugged");
+ info("model %04x:%04x detected", udev->descriptor.idVendor,
+ udev->descriptor.idProduct);
+
+ if ((cam =
+ kmalloc(sizeof(struct zr364xx_camera), GFP_KERNEL)) == NULL) {
+ info("cam: out of memory !");
+ return -ENODEV;
+ }
+ memset(cam, 0x00, sizeof(struct zr364xx_camera));
+ /* save the init method used by this camera */
+ cam->method = id->driver_info;
+
+ cam->vdev = video_device_alloc();
+ if (cam->vdev == NULL) {
+ info("cam->vdev: out of memory !");
+ kfree(cam);
+ return -ENODEV;
+ }
+ memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template));
+ video_set_drvdata(cam->vdev, cam);
+ if (debug)
+ cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+
+ cam->udev = udev;
+
+ if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) {
+ info("cam->buffer: out of memory !");
+ video_device_release(cam->vdev);
+ kfree(cam);
+ return -ENODEV;
+ }
+
+ switch (mode) {
+ case 1:
+ info("160x120 mode selected");
+ cam->width = 160;
+ cam->height = 120;
+ break;
+ case 2:
+ info("640x480 mode selected");
+ cam->width = 640;
+ cam->height = 480;
+ break;
+ default:
+ info("320x240 mode selected");
+ cam->width = 320;
+ cam->height = 240;
+ break;
+ }
+
+ m0d1[0] = mode;
+ m1[2].value = 0xf000 + mode;
+ m2[1].value = 0xf000 + mode;
+ header2[437] = cam->height / 256;
+ header2[438] = cam->height % 256;
+ header2[439] = cam->width / 256;
+ header2[440] = cam->width % 256;
+
+ cam->nb = 0;
+ cam->brightness = 64;
+ mutex_init(&cam->lock);
+
+ if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1) == -1) {
+ info("video_register_device failed");
+ video_device_release(cam->vdev);
+ kfree(cam->buffer);
+ kfree(cam);
+ return -ENODEV;
+ }
+
+ usb_set_intfdata(intf, cam);
+
+ info(DRIVER_DESC " controlling video device %d", cam->vdev->minor);
+ return 0;
+}
+
+
+static void zr364xx_disconnect(struct usb_interface *intf)
+{
+ struct zr364xx_camera *cam = usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
+ dev_set_drvdata(&intf->dev, NULL);
+ info(DRIVER_DESC " webcam unplugged");
+ if (cam->vdev)
+ video_unregister_device(cam->vdev);
+ cam->vdev = NULL;
+ kfree(cam->buffer);
+ if (cam->framebuf)
+ vfree(cam->framebuf);
+ kfree(cam);
+}
+
+
+
+/**********************/
+/* Module integration */
+/**********************/
+
+static struct usb_driver zr364xx_driver = {
+ .name = "zr364xx",
+ .probe = zr364xx_probe,
+ .disconnect = zr364xx_disconnect,
+ .id_table = device_table
+};
+
+
+static int __init zr364xx_init(void)
+{
+ int retval;
+ retval = usb_register(&zr364xx_driver) < 0;
+ if (retval)
+ info("usb_register failed!");
+ else
+ info(DRIVER_DESC " module loaded");
+ return retval;
+}
+
+
+static void __exit zr364xx_exit(void)
+{
+ info(DRIVER_DESC " module unloaded");
+ usb_deregister(&zr364xx_driver);
+}
+
+
+module_init(zr364xx_init);
+module_exit(zr364xx_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c
index b691292ff599..7dd34bd28efc 100644
--- a/drivers/message/fusion/mptlan.c
+++ b/drivers/message/fusion/mptlan.c
@@ -714,6 +714,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
LANSendRequest_t *pSendReq;
SGETransaction32_t *pTrans;
SGESimple64_t *pSimple;
+ const unsigned char *mac;
dma_addr_t dma;
unsigned long flags;
int ctx;
@@ -753,7 +754,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
/* Set the mac.raw pointer, since this apparently isn't getting
* done before we get the skb. Pull the data pointer past the mac data.
*/
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, 12);
dma = pci_map_single(mpt_dev->pcidev, skb->data, skb->len,
@@ -784,6 +785,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
// IOC_AND_NETDEV_NAMES_s_s(dev),
// ctx, skb, skb->data));
+ mac = skb_mac_header(skb);
#ifdef QLOGIC_NAA_WORKAROUND
{
struct NAA_Hosed *nh;
@@ -793,12 +795,12 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
drops. */
read_lock_irq(&bad_naa_lock);
for (nh = mpt_bad_naa; nh != NULL; nh=nh->next) {
- if ((nh->ieee[0] == skb->mac.raw[0]) &&
- (nh->ieee[1] == skb->mac.raw[1]) &&
- (nh->ieee[2] == skb->mac.raw[2]) &&
- (nh->ieee[3] == skb->mac.raw[3]) &&
- (nh->ieee[4] == skb->mac.raw[4]) &&
- (nh->ieee[5] == skb->mac.raw[5])) {
+ if ((nh->ieee[0] == mac[0]) &&
+ (nh->ieee[1] == mac[1]) &&
+ (nh->ieee[2] == mac[2]) &&
+ (nh->ieee[3] == mac[3]) &&
+ (nh->ieee[4] == mac[4]) &&
+ (nh->ieee[5] == mac[5])) {
cur_naa = nh->NAA;
dlprintk ((KERN_INFO "mptlan/sdu_send: using NAA value "
"= %04x.\n", cur_naa));
@@ -810,12 +812,12 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
#endif
pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa << 16) |
- (skb->mac.raw[0] << 8) |
- (skb->mac.raw[1] << 0));
- pTrans->TransactionDetails[1] = cpu_to_le32((skb->mac.raw[2] << 24) |
- (skb->mac.raw[3] << 16) |
- (skb->mac.raw[4] << 8) |
- (skb->mac.raw[5] << 0));
+ (mac[0] << 8) |
+ (mac[1] << 0));
+ pTrans->TransactionDetails[1] = cpu_to_le32((mac[2] << 24) |
+ (mac[3] << 16) |
+ (mac[4] << 8) |
+ (mac[5] << 0));
pSimple = (SGESimple64_t *) &pTrans->TransactionDetails[2];
@@ -930,7 +932,7 @@ mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg)
pci_dma_sync_single_for_cpu(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
- memcpy(skb_put(skb, len), old_skb->data, len);
+ skb_copy_from_linear_data(old_skb, skb_put(skb, len), len);
pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
@@ -1091,7 +1093,7 @@ mpt_lan_receive_post_reply(struct net_device *dev,
priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len,
PCI_DMA_FROMDEVICE);
- memcpy(skb_put(skb, l), old_skb->data, l);
+ skb_copy_from_linear_data(old_skb, skb_put(skb, l), l);
pci_dma_sync_single_for_device(mpt_dev->pcidev,
priv->RcvCtl[ctx].dma,
@@ -1120,7 +1122,7 @@ mpt_lan_receive_post_reply(struct net_device *dev,
priv->RcvCtl[ctx].len,
PCI_DMA_FROMDEVICE);
- memcpy(skb_put(skb, len), old_skb->data, len);
+ skb_copy_from_linear_data(old_skb, skb_put(skb, len), len);
pci_dma_sync_single_for_device(mpt_dev->pcidev,
priv->RcvCtl[ctx].dma,
@@ -1549,7 +1551,7 @@ mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev)
struct mpt_lan_ohdr *fch = (struct mpt_lan_ohdr *)skb->data;
struct fcllc *fcllc;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, sizeof(struct mpt_lan_ohdr));
if (fch->dtype == htons(0xffff)) {
diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c
index d32698b02d7f..e0e82d849d5f 100644
--- a/drivers/mmc/mmc_sysfs.c
+++ b/drivers/mmc/mmc_sysfs.c
@@ -86,31 +86,26 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
{
struct mmc_card *card = dev_to_mmc_card(dev);
char ccc[13];
- int i = 0;
-
-#define add_env(fmt,val) \
- ({ \
- int len, ret = -ENOMEM; \
- if (i < num_envp) { \
- envp[i++] = buf; \
- len = snprintf(buf, buf_size, fmt, val) + 1; \
- buf_size -= len; \
- buf += len; \
- if (buf_size >= 0) \
- ret = 0; \
- } \
- ret; \
- })
+ int retval = 0, i = 0, length = 0;
+
+#define add_env(fmt,val) do { \
+ retval = add_uevent_var(envp, num_envp, &i, \
+ buf, buf_size, &length, \
+ fmt, val); \
+ if (retval) \
+ return retval; \
+} while (0);
for (i = 0; i < 12; i++)
ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0';
ccc[12] = '\0';
- i = 0;
add_env("MMC_CCC=%s", ccc);
add_env("MMC_MANFID=%06x", card->cid.manfid);
add_env("MMC_NAME=%s", mmc_card_name(card));
add_env("MMC_OEMID=%04x", card->cid.oemid);
+#undef add_env
+ envp[i] = NULL;
return 0;
}
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 26f75c299440..c1b47db29bd2 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -1,8 +1,6 @@
# $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $
-menu "Memory Technology Devices (MTD)"
-
-config MTD
+menuconfig MTD
tristate "Memory Technology Device (MTD) support"
help
Memory Technology Devices are flash, RAM and similar chips, often
@@ -13,9 +11,10 @@ config MTD
them. It will also allow you to select individual drivers for
particular hardware and users of MTD devices. If unsure, say N.
+if MTD
+
config MTD_DEBUG
bool "Debugging"
- depends on MTD
help
This turns on low-level debugging for the entire MTD sub-system.
Normally, you should say 'N'.
@@ -29,7 +28,6 @@ config MTD_DEBUG_VERBOSE
config MTD_CONCAT
tristate "MTD concatenating support"
- depends on MTD
help
Support for concatenating several MTD devices into a single
(virtual) one. This allows you to have -for example- a JFFS(2)
@@ -38,7 +36,6 @@ config MTD_CONCAT
config MTD_PARTITIONS
bool "MTD partitioning support"
- depends on MTD
help
If you have a device which needs to divide its flash chip(s) up
into multiple 'partitions', each of which appears to the user as
@@ -153,11 +150,9 @@ config MTD_AFS_PARTS
'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
comment "User Modules And Translation Layers"
- depends on MTD
config MTD_CHAR
tristate "Direct char device access to MTD devices"
- depends on MTD
help
This provides a character device for each MTD device present in
the system, allowing the user to read and write directly to the
@@ -166,12 +161,12 @@ config MTD_CHAR
config MTD_BLKDEVS
tristate "Common interface to block layer for MTD 'translation layers'"
- depends on MTD && BLOCK
+ depends on BLOCK
default n
config MTD_BLOCK
tristate "Caching block device access to MTD devices"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
Although most flash chips have an erase size too large to be useful
@@ -194,7 +189,7 @@ config MTD_BLOCK
config MTD_BLOCK_RO
tristate "Readonly block device access to MTD devices"
- depends on MTD_BLOCK!=y && MTD && BLOCK
+ depends on MTD_BLOCK!=y && BLOCK
select MTD_BLKDEVS
help
This allows you to mount read-only file systems (such as cramfs)
@@ -206,7 +201,7 @@ config MTD_BLOCK_RO
config FTL
tristate "FTL (Flash Translation Layer) support"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
This provides support for the original Flash Translation Layer which
@@ -223,7 +218,7 @@ config FTL
config NFTL
tristate "NFTL (NAND Flash Translation Layer) support"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
This provides support for the NAND Flash Translation Layer which is
@@ -247,7 +242,7 @@ config NFTL_RW
config INFTL
tristate "INFTL (Inverse NAND Flash Translation Layer) support"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
This provides support for the Inverse NAND Flash Translation
@@ -265,7 +260,7 @@ config INFTL
config RFD_FTL
tristate "Resident Flash Disk (Flash Translation Layer) support"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
This provides support for the flash translation layer known
@@ -276,7 +271,7 @@ config RFD_FTL
config SSFDC
tristate "NAND SSFDC (SmartMedia) read only translation layer"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
help
This enables read only access to SmartMedia formatted NAND
@@ -292,5 +287,6 @@ source "drivers/mtd/nand/Kconfig"
source "drivers/mtd/onenand/Kconfig"
-endmenu
+source "drivers/mtd/ubi/Kconfig"
+endif # MTD
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index c130e6261adf..92055405cb30 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -28,3 +28,5 @@ nftl-objs := nftlcore.o nftlmount.o
inftl-objs := inftlcore.o inftlmount.o
obj-y += chips/ maps/ devices/ nand/ onenand/
+
+obj-$(CONFIG_MTD_UBI) += ubi/
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 72e6d73beb40..d28e0fc85e12 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -6,7 +6,6 @@ menu "RAM/ROM/Flash chip drivers"
config MTD_CFI
tristate "Detect flash chips by Common Flash Interface (CFI) probe"
- depends on MTD
select MTD_GEN_PROBE
help
The Common Flash Interface specification was developed by Intel,
@@ -18,7 +17,6 @@ config MTD_CFI
config MTD_JEDECPROBE
tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
- depends on MTD
select MTD_GEN_PROBE
help
This option enables JEDEC-style probing of flash chips which are not
@@ -213,21 +211,18 @@ config MTD_CFI_UTIL
config MTD_RAM
tristate "Support for RAM chips in bus mapping"
- depends on MTD
help
This option enables basic support for RAM chips accessed through
a bus mapping driver.
config MTD_ROM
tristate "Support for ROM chips in bus mapping"
- depends on MTD
help
This option enables basic support for ROM chips accessed through
a bus mapping driver.
config MTD_ABSENT
tristate "Support for absent chips in bus mapping"
- depends on MTD
help
This option enables support for a dummy probing driver used to
allocated placeholder MTD devices on systems that have socketed
@@ -237,7 +232,6 @@ config MTD_ABSENT
with this driver will return -ENODEV upon access.
config MTD_OBSOLETE_CHIPS
- depends on MTD
bool "Older (theoretically obsoleted now) drivers for non-CFI chips"
help
This option does not enable any code directly, but will allow you to
@@ -250,7 +244,7 @@ config MTD_OBSOLETE_CHIPS
config MTD_AMDSTD
tristate "AMD compatible flash chip support (non-CFI)"
- depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
+ depends on MTD_OBSOLETE_CHIPS && BROKEN
help
This option enables support for flash chips using AMD-compatible
commands, including some which are not CFI-compatible and hence
@@ -260,7 +254,7 @@ config MTD_AMDSTD
config MTD_SHARP
tristate "pre-CFI Sharp chip support"
- depends on MTD && MTD_OBSOLETE_CHIPS
+ depends on MTD_OBSOLETE_CHIPS
help
This option enables support for flash chips using Sharp-compatible
commands, including some which are not CFI-compatible and hence
@@ -268,7 +262,7 @@ config MTD_SHARP
config MTD_JEDEC
tristate "JEDEC device support"
- depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
+ depends on MTD_OBSOLETE_CHIPS && BROKEN
help
Enable older JEDEC flash interface devices for self
programming flash. It is commonly used in older AMD chips. It is
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index f334959a335b..2f19fa78d24a 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -15,6 +15,8 @@
* - optimized write buffer method
* 02/05/2002 Christopher Hoover <ch@hpl.hp.com>/<ch@murgatroid.com>
* - reworked lock/unlock/erase support for var size flash
+ * 21/03/2007 Rodolfo Giometti <giometti@linux.it>
+ * - auto unlock sectors on resume for auto locking flash on power up
*/
#include <linux/module.h>
@@ -30,6 +32,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
+#include <linux/bitmap.h>
#include <linux/mtd/xip.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
@@ -220,6 +223,15 @@ static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
}
}
+/*
+ * Some chips power-up with all sectors locked by default.
+ */
+static void fixup_use_powerup_lock(struct mtd_info *mtd, void *param)
+{
+ printk(KERN_INFO "Using auto-unlock on power-up/resume\n" );
+ mtd->flags |= MTD_STUPID_LOCK;
+}
+
static struct cfi_fixup cfi_fixup_table[] = {
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },
@@ -232,6 +244,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
#endif
{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
+ { MANUFACTURER_INTEL, 0x891c, fixup_use_powerup_lock, NULL, },
{ 0, 0, NULL, NULL }
};
@@ -460,6 +473,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
+ mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL);
}
offset += (ersize * ernum);
}
@@ -1825,8 +1839,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd)
}
}
-#ifdef DEBUG_LOCK_BITS
-static int __xipram do_printlockstatus_oneblock(struct map_info *map,
+static int __xipram do_getlockstatus_oneblock(struct map_info *map,
struct flchip *chip,
unsigned long adr,
int len, void *thunk)
@@ -1840,8 +1853,17 @@ static int __xipram do_printlockstatus_oneblock(struct map_info *map,
chip->state = FL_JEDEC_QUERY;
status = cfi_read_query(map, adr+(2*ofs_factor));
xip_enable(map, chip, 0);
+ return status;
+}
+
+#ifdef DEBUG_LOCK_BITS
+static int __xipram do_printlockstatus_oneblock(struct map_info *map,
+ struct flchip *chip,
+ unsigned long adr,
+ int len, void *thunk)
+{
printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
- adr, status);
+ adr, do_getlockstatus_oneblock(map, chip, adr, len, thunk));
return 0;
}
#endif
@@ -2216,14 +2238,45 @@ static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
#endif
+static void cfi_intelext_save_locks(struct mtd_info *mtd)
+{
+ struct mtd_erase_region_info *region;
+ int block, status, i;
+ unsigned long adr;
+ size_t len;
+
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ region = &mtd->eraseregions[i];
+ if (!region->lockmap)
+ continue;
+
+ for (block = 0; block < region->numblocks; block++){
+ len = region->erasesize;
+ adr = region->offset + block * len;
+
+ status = cfi_varsize_frob(mtd,
+ do_getlockstatus_oneblock, adr, len, 0);
+ if (status)
+ set_bit(block, region->lockmap);
+ else
+ clear_bit(block, region->lockmap);
+ }
+ }
+}
+
static int cfi_intelext_suspend(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
int i;
struct flchip *chip;
int ret = 0;
+ if ((mtd->flags & MTD_STUPID_LOCK)
+ && extp && (extp->FeatureSupport & (1 << 5)))
+ cfi_intelext_save_locks(mtd);
+
for (i=0; !ret && i<cfi->numchips; i++) {
chip = &cfi->chips[i];
@@ -2285,10 +2338,33 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
return ret;
}
+static void cfi_intelext_restore_locks(struct mtd_info *mtd)
+{
+ struct mtd_erase_region_info *region;
+ int block, i;
+ unsigned long adr;
+ size_t len;
+
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ region = &mtd->eraseregions[i];
+ if (!region->lockmap)
+ continue;
+
+ for (block = 0; block < region->numblocks; block++) {
+ len = region->erasesize;
+ adr = region->offset + block * len;
+
+ if (!test_bit(block, region->lockmap))
+ cfi_intelext_unlock(mtd, adr, len);
+ }
+ }
+}
+
static void cfi_intelext_resume(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
int i;
struct flchip *chip;
@@ -2307,6 +2383,10 @@ static void cfi_intelext_resume(struct mtd_info *mtd)
spin_unlock(chip->mutex);
}
+
+ if ((mtd->flags & MTD_STUPID_LOCK)
+ && extp && (extp->FeatureSupport & (1 << 5)))
+ cfi_intelext_restore_locks(mtd);
}
static int cfi_intelext_reset(struct mtd_info *mtd)
@@ -2347,12 +2427,19 @@ static void cfi_intelext_destroy(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
+ struct mtd_erase_region_info *region;
+ int i;
cfi_intelext_reset(mtd);
unregister_reboot_notifier(&mtd->reboot_notifier);
kfree(cfi->cmdset_priv);
kfree(cfi->cfiq);
kfree(cfi->chips[0].priv);
kfree(cfi);
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ region = &mtd->eraseregions[i];
+ if (region->lockmap)
+ kfree(region->lockmap);
+ }
kfree(mtd->eraseregions);
}
diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h
index 77303ce5dcf1..ab44f2b996f8 100644
--- a/drivers/mtd/chips/fwh_lock.h
+++ b/drivers/mtd/chips/fwh_lock.h
@@ -65,11 +65,12 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
return ret;
}
+ chip->oldstate = chip->state;
chip->state = xxlt->state;
map_write(map, CMD(xxlt->val), adr);
/* Done and happy. */
- chip->state = FL_READY;
+ chip->state = chip->oldstate;
put_chip(map, chip, adr);
spin_unlock(chip->mutex);
return 0;
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 440f6851da69..690c94236d7f 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -6,7 +6,7 @@ menu "Self-contained MTD device drivers"
config MTD_PMC551
tristate "Ramix PMC551 PCI Mezzanine RAM card support"
- depends on MTD && PCI
+ depends on PCI
---help---
This provides a MTD device driver for the Ramix PMC551 RAM PCI card
from Ramix Inc. <http://www.ramix.com/products/memory/pmc551.html>.
@@ -40,7 +40,7 @@ config MTD_PMC551_DEBUG
config MTD_MS02NV
tristate "DEC MS02-NV NVRAM module support"
- depends on MTD && MACH_DECSTATION
+ depends on MACH_DECSTATION
help
This is an MTD driver for the DEC's MS02-NV (54-20948-01) battery
backed-up NVRAM module. The module was originally meant as an NFS
@@ -54,15 +54,23 @@ config MTD_MS02NV
config MTD_DATAFLASH
tristate "Support for AT45xxx DataFlash"
- depends on MTD && SPI_MASTER && EXPERIMENTAL
+ depends on SPI_MASTER && EXPERIMENTAL
help
This enables access to AT45xxx DataFlash chips, using SPI.
Sometimes DataFlash chips are packaged inside MMC-format
cards; at this writing, the MMC stack won't handle those.
+config MTD_DATAFLASH26
+ tristate "AT91RM9200 DataFlash AT26xxx"
+ depends on MTD && ARCH_AT91RM9200 && AT91_SPI
+ help
+ This enables access to the DataFlash chip (AT26xxx) on an
+ AT91RM9200-based board.
+ If you have such a board and such a DataFlash, say 'Y'.
+
config MTD_M25P80
tristate "Support for M25 SPI Flash"
- depends on MTD && SPI_MASTER && EXPERIMENTAL
+ depends on SPI_MASTER && EXPERIMENTAL
help
This enables access to ST M25P80 and similar SPI flash chips,
used for program and data storage. Set up your spi devices
@@ -70,7 +78,6 @@ config MTD_M25P80
config MTD_SLRAM
tristate "Uncached system RAM"
- depends on MTD
help
If your CPU cannot cache all of the physical memory in your machine,
you can still use it for storage or swap by using this driver to
@@ -78,7 +85,6 @@ config MTD_SLRAM
config MTD_PHRAM
tristate "Physical system RAM"
- depends on MTD
help
This is a re-implementation of the slram driver above.
@@ -88,7 +94,7 @@ config MTD_PHRAM
config MTD_LART
tristate "28F160xx flash driver for LART"
- depends on SA1100_LART && MTD
+ depends on SA1100_LART
help
This enables the flash driver for LART. Please note that you do
not need any mapping/chip driver for LART. This one does it all
@@ -96,7 +102,6 @@ config MTD_LART
config MTD_MTDRAM
tristate "Test driver using RAM"
- depends on MTD
help
This enables a test MTD device driver which uses vmalloc() to
provide storage. You probably want to say 'N' unless you're
@@ -136,7 +141,7 @@ config MTDRAM_ABS_POS
config MTD_BLOCK2MTD
tristate "MTD using block device"
- depends on MTD && BLOCK
+ depends on BLOCK
help
This driver allows a block device to appear as an MTD. It would
generally be used in the following cases:
@@ -150,7 +155,6 @@ comment "Disk-On-Chip Device Drivers"
config MTD_DOC2000
tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)"
- depends on MTD
select MTD_DOCPROBE
select MTD_NAND_IDS
---help---
@@ -173,7 +177,6 @@ config MTD_DOC2000
config MTD_DOC2001
tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)"
- depends on MTD
select MTD_DOCPROBE
select MTD_NAND_IDS
---help---
@@ -195,7 +198,6 @@ config MTD_DOC2001
config MTD_DOC2001PLUS
tristate "M-Systems Disk-On-Chip Millennium Plus"
- depends on MTD
select MTD_DOCPROBE
select MTD_NAND_IDS
---help---
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 0f788d5c4bf8..8ab568b3f533 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,4 +16,5 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
+obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c
new file mode 100644
index 000000000000..64ce37f986fc
--- /dev/null
+++ b/drivers/mtd/devices/at91_dataflash26.c
@@ -0,0 +1,485 @@
+/*
+ * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder)
+ * This is a largely modified version of at91_dataflash.c that
+ * supports AT26xxx dataflash chips. The original driver supports
+ * AT45xxx chips.
+ *
+ * Note: This driver was only tested with an AT26F004. It should be
+ * easy to make it work with other AT26xxx dataflash devices, though.
+ *
+ * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de>
+ * original Copyright (C) SAN People (Pty) Ltd
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+
+#include <asm/arch/at91_spi.h>
+
+#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */
+
+#define MANUFACTURER_ID_ATMEL 0x1F
+
+/* command codes */
+
+#define AT26_OP_READ_STATUS 0x05
+#define AT26_OP_READ_DEV_ID 0x9F
+#define AT26_OP_ERASE_PAGE_4K 0x20
+#define AT26_OP_READ_ARRAY_FAST 0x0B
+#define AT26_OP_SEQUENTIAL_WRITE 0xAF
+#define AT26_OP_WRITE_ENABLE 0x06
+#define AT26_OP_WRITE_DISABLE 0x04
+#define AT26_OP_SECTOR_PROTECT 0x36
+#define AT26_OP_SECTOR_UNPROTECT 0x39
+
+/* status register bits */
+
+#define AT26_STATUS_BUSY 0x01
+#define AT26_STATUS_WRITE_ENABLE 0x02
+
+struct dataflash_local
+{
+ int spi; /* SPI chip-select number */
+ unsigned int page_size; /* number of bytes per page */
+};
+
+
+/* Detected DataFlash devices */
+static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES];
+static int nr_devices = 0;
+
+/* Allocate a single SPI transfer descriptor. We're assuming that if multiple
+ SPI transfers occur at the same time, spi_access_bus() will serialize them.
+ If this is not valid, then either (i) each dataflash 'priv' structure
+ needs it's own transfer descriptor, (ii) we lock this one, or (iii) use
+ another mechanism. */
+static struct spi_transfer_list* spi_transfer_desc;
+
+/*
+ * Perform a SPI transfer to access the DataFlash device.
+ */
+static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len,
+ char* txnext, int txnext_len, char* rxnext, int rxnext_len)
+{
+ struct spi_transfer_list* list = spi_transfer_desc;
+
+ list->tx[0] = tx; list->txlen[0] = tx_len;
+ list->rx[0] = rx; list->rxlen[0] = rx_len;
+
+ list->tx[1] = txnext; list->txlen[1] = txnext_len;
+ list->rx[1] = rxnext; list->rxlen[1] = rxnext_len;
+
+ list->nr_transfers = nr;
+ /* Note: spi_transfer() always returns 0, there are no error checks */
+ return spi_transfer(list);
+}
+
+/*
+ * Return the status of the DataFlash device.
+ */
+static unsigned char at91_dataflash26_status(void)
+{
+ unsigned char command[2];
+
+ command[0] = AT26_OP_READ_STATUS;
+ command[1] = 0;
+
+ do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
+
+ return command[1];
+}
+
+/*
+ * Poll the DataFlash device until it is READY.
+ */
+static unsigned char at91_dataflash26_waitready(void)
+{
+ unsigned char status;
+
+ while (1) {
+ status = at91_dataflash26_status();
+ if (!(status & AT26_STATUS_BUSY))
+ return status;
+ }
+}
+
+/*
+ * Enable/disable write access
+ */
+ static void at91_dataflash26_write_enable(int enable)
+{
+ unsigned char cmd[2];
+
+ DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable);
+
+ if (enable)
+ cmd[0] = AT26_OP_WRITE_ENABLE;
+ else
+ cmd[0] = AT26_OP_WRITE_DISABLE;
+ cmd[1] = 0;
+
+ do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
+}
+
+/*
+ * Protect/unprotect sector
+ */
+ static void at91_dataflash26_sector_protect(loff_t addr, int protect)
+{
+ unsigned char cmd[4];
+
+ DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n",
+ addr, protect);
+
+ if (protect)
+ cmd[0] = AT26_OP_SECTOR_PROTECT;
+ else
+ cmd[0] = AT26_OP_SECTOR_UNPROTECT;
+ cmd[1] = (addr & 0x00FF0000) >> 16;
+ cmd[2] = (addr & 0x0000FF00) >> 8;
+ cmd[3] = (addr & 0x000000FF);
+
+ do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
+}
+
+/*
+ * Erase blocks of flash.
+ */
+static int at91_dataflash26_erase(struct mtd_info *mtd,
+ struct erase_info *instr)
+{
+ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+ unsigned char cmd[4];
+
+ DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n",
+ instr->addr, instr->len);
+
+ /* Sanity checks */
+ if (priv->page_size != 4096)
+ return -EINVAL; /* Can't handle other sizes at the moment */
+
+ if ( ((instr->len % mtd->erasesize) != 0)
+ || ((instr->len % priv->page_size) != 0)
+ || ((instr->addr % priv->page_size) != 0)
+ || ((instr->addr + instr->len) > mtd->size))
+ return -EINVAL;
+
+ spi_access_bus(priv->spi);
+
+ while (instr->len > 0) {
+ at91_dataflash26_write_enable(1);
+ at91_dataflash26_sector_protect(instr->addr, 0);
+ at91_dataflash26_write_enable(1);
+ cmd[0] = AT26_OP_ERASE_PAGE_4K;
+ cmd[1] = (instr->addr & 0x00FF0000) >> 16;
+ cmd[2] = (instr->addr & 0x0000FF00) >> 8;
+ cmd[3] = (instr->addr & 0x000000FF);
+
+ DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x"
+ "0x%02x\n",
+ cmd[0], cmd[1], cmd[2], cmd[3]);
+
+ do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
+ at91_dataflash26_waitready();
+
+ instr->addr += priv->page_size; /* next page */
+ instr->len -= priv->page_size;
+ }
+
+ at91_dataflash26_write_enable(0);
+ spi_release_bus(priv->spi);
+
+ /* Inform MTD subsystem that erase is complete */
+ instr->state = MTD_ERASE_DONE;
+ if (instr->callback)
+ instr->callback(instr);
+
+ return 0;
+}
+
+/*
+ * Read from the DataFlash device.
+ * from : Start offset in flash device
+ * len : Number of bytes to read
+ * retlen : Number of bytes actually read
+ * buf : Buffer that will receive data
+ */
+static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+ unsigned char cmd[5];
+
+ DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n",
+ from, from+len);
+
+ *retlen = 0;
+
+ /* Sanity checks */
+ if (!len)
+ return 0;
+ if (from + len > mtd->size)
+ return -EINVAL;
+
+ cmd[0] = AT26_OP_READ_ARRAY_FAST;
+ cmd[1] = (from & 0x00FF0000) >> 16;
+ cmd[2] = (from & 0x0000FF00) >> 8;
+ cmd[3] = (from & 0x000000FF);
+ /* cmd[4] is a "Don't care" byte */
+
+ DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n",
+ cmd[0], cmd[1], cmd[2], cmd[3]);
+
+ spi_access_bus(priv->spi);
+ do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len);
+ spi_release_bus(priv->spi);
+
+ *retlen = len;
+ return 0;
+}
+
+/*
+ * Write to the DataFlash device.
+ * to : Start offset in flash device
+ * len : Number of bytes to write
+ * retlen : Number of bytes actually written
+ * buf : Buffer containing the data
+ */
+static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+ unsigned int addr, buf_index = 0;
+ int ret = -EIO, sector, last_sector;
+ unsigned char status, cmd[5];
+
+ DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len);
+
+ *retlen = 0;
+
+ /* Sanity checks */
+ if (!len)
+ return 0;
+ if (to + len > mtd->size)
+ return -EINVAL;
+
+ spi_access_bus(priv->spi);
+
+ addr = to;
+ last_sector = -1;
+
+ while (buf_index < len) {
+ sector = addr / priv->page_size;
+ /* Write first byte if a new sector begins */
+ if (sector != last_sector) {
+ at91_dataflash26_write_enable(1);
+ at91_dataflash26_sector_protect(addr, 0);
+ at91_dataflash26_write_enable(1);
+
+ /* Program first byte of a new sector */
+ cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
+ cmd[1] = (addr & 0x00FF0000) >> 16;
+ cmd[2] = (addr & 0x0000FF00) >> 8;
+ cmd[3] = (addr & 0x000000FF);
+ cmd[4] = buf[buf_index++];
+ do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
+ status = at91_dataflash26_waitready();
+ addr++;
+ /* On write errors, the chip resets the write enable
+ flag. This also happens after the last byte of a
+ sector is successfully programmed. */
+ if ( ( !(status & AT26_STATUS_WRITE_ENABLE))
+ && ((addr % priv->page_size) != 0) ) {
+ DEBUG(MTD_DEBUG_LEVEL1,
+ "write error1: addr=0x%06x, "
+ "status=0x%02x\n", addr, status);
+ goto write_err;
+ }
+ (*retlen)++;
+ last_sector = sector;
+ }
+
+ /* Write subsequent bytes in the same sector */
+ cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
+ cmd[1] = buf[buf_index++];
+ do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
+ status = at91_dataflash26_waitready();
+ addr++;
+
+ if ( ( !(status & AT26_STATUS_WRITE_ENABLE))
+ && ((addr % priv->page_size) != 0) ) {
+ DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, "
+ "status=0x%02x\n", addr, status);
+ goto write_err;
+ }
+
+ (*retlen)++;
+ }
+
+ ret = 0;
+ at91_dataflash26_write_enable(0);
+write_err:
+ spi_release_bus(priv->spi);
+ return ret;
+}
+
+/*
+ * Initialize and register DataFlash device with MTD subsystem.
+ */
+static int __init add_dataflash(int channel, char *name, int nr_pages,
+ int pagesize)
+{
+ struct mtd_info *device;
+ struct dataflash_local *priv;
+
+ if (nr_devices >= DATAFLASH_MAX_DEVICES) {
+ printk(KERN_ERR "at91_dataflash26: Too many devices "
+ "detected\n");
+ return 0;
+ }
+
+ device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8,
+ GFP_KERNEL);
+ if (!device)
+ return -ENOMEM;
+
+ device->name = (char *)&device[1];
+ sprintf(device->name, "%s.spi%d", name, channel);
+ device->size = nr_pages * pagesize;
+ device->erasesize = pagesize;
+ device->owner = THIS_MODULE;
+ device->type = MTD_DATAFLASH;
+ device->flags = MTD_CAP_NORFLASH;
+ device->erase = at91_dataflash26_erase;
+ device->read = at91_dataflash26_read;
+ device->write = at91_dataflash26_write;
+
+ priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local),
+ GFP_KERNEL);
+ if (!priv) {
+ kfree(device);
+ return -ENOMEM;
+ }
+
+ priv->spi = channel;
+ priv->page_size = pagesize;
+ device->priv = priv;
+
+ mtd_devices[nr_devices] = device;
+ nr_devices++;
+ printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n",
+ name, channel, device->size);
+
+ return add_mtd_device(device);
+}
+
+/*
+ * Detect and initialize DataFlash device connected to specified SPI channel.
+ *
+ */
+
+struct dataflash26_types {
+ unsigned char id0;
+ unsigned char id1;
+ char *name;
+ int pagesize;
+ int nr_pages;
+};
+
+struct dataflash26_types df26_types[] = {
+ {
+ .id0 = 0x04,
+ .id1 = 0x00,
+ .name = "AT26F004",
+ .pagesize = 4096,
+ .nr_pages = 128,
+ },
+ {
+ .id0 = 0x45,
+ .id1 = 0x01,
+ .name = "AT26DF081A", /* Not tested ! */
+ .pagesize = 4096,
+ .nr_pages = 256,
+ },
+};
+
+static int __init at91_dataflash26_detect(int channel)
+{
+ unsigned char status, cmd[5];
+ int i;
+
+ spi_access_bus(channel);
+ status = at91_dataflash26_status();
+
+ if (status == 0 || status == 0xff) {
+ printk(KERN_ERR "at91_dataflash26_detect: status error %d\n",
+ status);
+ spi_release_bus(channel);
+ return -ENODEV;
+ }
+
+ cmd[0] = AT26_OP_READ_DEV_ID;
+ do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
+ spi_release_bus(channel);
+
+ if (cmd[1] != MANUFACTURER_ID_ATMEL)
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(df26_types); i++) {
+ if ( cmd[2] == df26_types[i].id0
+ && cmd[3] == df26_types[i].id1)
+ return add_dataflash(channel,
+ df26_types[i].name,
+ df26_types[i].nr_pages,
+ df26_types[i].pagesize);
+ }
+
+ printk(KERN_ERR "at91_dataflash26_detect: Unsupported device "
+ "(0x%02x/0x%02x)\n", cmd[2], cmd[3]);
+ return -ENODEV;
+}
+
+static int __init at91_dataflash26_init(void)
+{
+ spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list),
+ GFP_KERNEL);
+ if (!spi_transfer_desc)
+ return -ENOMEM;
+
+ /* DataFlash (SPI chip select 0) */
+ at91_dataflash26_detect(0);
+
+#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD
+ /* DataFlash card (SPI chip select 3) */
+ at91_dataflash26_detect(3);
+#endif
+ return 0;
+}
+
+static void __exit at91_dataflash26_exit(void)
+{
+ int i;
+
+ for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) {
+ if (mtd_devices[i]) {
+ del_mtd_device(mtd_devices[i]);
+ kfree(mtd_devices[i]->priv);
+ kfree(mtd_devices[i]);
+ }
+ }
+ nr_devices = 0;
+ kfree(spi_transfer_desc);
+}
+
+module_init(at91_dataflash26_init);
+module_exit(at91_dataflash26_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hans J. Koch");
+MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200");
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index f9f2ce7806b0..ce47544dc120 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -40,56 +40,9 @@ struct block2mtd_dev {
static LIST_HEAD(blkmtd_device_list);
-#define PAGE_READAHEAD 64
-static void cache_readahead(struct address_space *mapping, int index)
+static struct page* page_read(struct address_space *mapping, int index)
{
filler_t *filler = (filler_t*)mapping->a_ops->readpage;
- int i, pagei;
- unsigned ret = 0;
- unsigned long end_index;
- struct page *page;
- LIST_HEAD(page_pool);
- struct inode *inode = mapping->host;
- loff_t isize = i_size_read(inode);
-
- if (!isize) {
- INFO("iSize=0 in cache_readahead\n");
- return;
- }
-
- end_index = ((isize - 1) >> PAGE_CACHE_SHIFT);
-
- read_lock_irq(&mapping->tree_lock);
- for (i = 0; i < PAGE_READAHEAD; i++) {
- pagei = index + i;
- if (pagei > end_index) {
- INFO("Overrun end of disk in cache readahead\n");
- break;
- }
- page = radix_tree_lookup(&mapping->page_tree, pagei);
- if (page && (!i))
- break;
- if (page)
- continue;
- read_unlock_irq(&mapping->tree_lock);
- page = page_cache_alloc_cold(mapping);
- read_lock_irq(&mapping->tree_lock);
- if (!page)
- break;
- page->index = pagei;
- list_add(&page->lru, &page_pool);
- ret++;
- }
- read_unlock_irq(&mapping->tree_lock);
- if (ret)
- read_cache_pages(mapping, &page_pool, filler, NULL);
-}
-
-
-static struct page* page_readahead(struct address_space *mapping, int index)
-{
- filler_t *filler = (filler_t*)mapping->a_ops->readpage;
- cache_readahead(mapping, index);
return read_cache_page(mapping, index, filler, NULL);
}
@@ -105,14 +58,14 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
u_long *max;
while (pages) {
- page = page_readahead(mapping, index);
+ page = page_read(mapping, index);
if (!page)
return -ENOMEM;
if (IS_ERR(page))
return PTR_ERR(page);
- max = (u_long*)page_address(page) + PAGE_SIZE;
- for (p=(u_long*)page_address(page); p<max; p++)
+ max = page_address(page) + PAGE_SIZE;
+ for (p=page_address(page); p<max; p++)
if (*p != -1UL) {
lock_page(page);
memset(page_address(page), 0xff, PAGE_SIZE);
@@ -174,8 +127,7 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
cpylen = len; // this page
len = len - cpylen;
- // Get page
- page = page_readahead(dev->blkdev->bd_inode->i_mapping, index);
+ page = page_read(dev->blkdev->bd_inode->i_mapping, index);
if (!page)
return -ENOMEM;
if (IS_ERR(page))
@@ -213,8 +165,7 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
cpylen = len; // this page
len = len - cpylen;
- // Get page
- page = page_readahead(mapping, index);
+ page = page_read(mapping, index);
if (!page)
return -ENOMEM;
if (IS_ERR(page))
@@ -308,9 +259,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
/* We might not have rootfs mounted at this point. Try
to resolve the device name by other means. */
- dev_t dev = name_to_dev_t(devname);
- if (dev != 0) {
- bdev = open_by_devnum(dev, FMODE_WRITE | FMODE_READ);
+ dev_t devt = name_to_dev_t(devname);
+ if (devt) {
+ bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ);
}
}
#endif
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index bbf0553bdb2e..d990d8141ef5 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -6,7 +6,6 @@ menu "Mapping drivers for chip access"
config MTD_COMPLEX_MAPPINGS
bool "Support non-linear mappings of flash chips"
- depends on MTD
help
This causes the chip drivers to allow for complicated
paged mappings of flash chips.
@@ -69,6 +68,39 @@ config MTD_PHYSMAP_OF
physically into the CPU's memory. The mapping description here is
taken from OF device tree.
+config MTD_PMC_MSP_EVM
+ tristate "CFI Flash device mapped on PMC-Sierra MSP"
+ depends on PMC_MSP && MTD_CFI
+ select MTD_PARTITIONS
+ help
+ This provides a 'mapping' driver which support the way
+ in which user-programmable flash chips are connected on the
+ PMC-Sierra MSP eval/demo boards
+
+choice
+ prompt "Maximum mappable memory avialable for flash IO"
+ depends on MTD_PMC_MSP_EVM
+ default MSP_FLASH_MAP_LIMIT_32M
+
+config MSP_FLASH_MAP_LIMIT_32M
+ bool "32M"
+
+endchoice
+
+config MSP_FLASH_MAP_LIMIT
+ hex
+ default "0x02000000"
+ depends on MSP_FLASH_MAP_LIMIT_32M
+
+config MTD_PMC_MSP_RAMROOT
+ tristate "Embedded RAM block device for root on PMC-Sierra MSP"
+ depends on PMC_MSP_EMBEDDED_ROOTFS && \
+ (MTD_BLOCK || MTD_BLOCK_RO) && \
+ MTD_RAM
+ help
+ This provides support for the embedded root file system
+ on PMC MSP devices. This memory is mapped as a MTD block device.
+
config MTD_SUN_UFLASH
tristate "Sun Microsystems userflash support"
depends on SPARC && MTD_CFI
@@ -240,13 +272,13 @@ config MTD_NETtel
config MTD_ALCHEMY
tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support"
- depends on SOC_AU1X00
+ depends on SOC_AU1X00 && MTD_PARTITIONS && MTD_CFI
help
Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards
config MTD_MTX1
tristate "4G Systems MTX-1 Flash device"
- depends on MIPS && MIPS_MTX1
+ depends on MIPS_MTX1 && MTD_CFI
help
Flash memory access on 4G Systems MTX-1 Board. If you have one of
these boards and would like to use the flash chips on it, say 'Y'.
@@ -384,7 +416,7 @@ config MTD_TQM834x
config MTD_OCELOT
tristate "Momenco Ocelot boot flash device"
- depends on MIPS && MOMENCO_OCELOT
+ depends on MOMENCO_OCELOT
help
This enables access routines for the boot flash device and for the
NVRAM on the Momenco Ocelot board. If you have one of these boards
@@ -517,7 +549,7 @@ config MTD_OMAP_NOR
# This needs CFI or JEDEC, depending on the cards found.
config MTD_PCI
tristate "PCI MTD driver"
- depends on MTD && PCI && MTD_COMPLEX_MAPPINGS
+ depends on PCI && MTD_COMPLEX_MAPPINGS
help
Mapping for accessing flash devices on add-in cards like the Intel XScale
IQ80310 card, and the Intel EBSA285 card in blank ROM programming mode
@@ -527,7 +559,7 @@ config MTD_PCI
config MTD_PCMCIA
tristate "PCMCIA MTD driver"
- depends on MTD && PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN
+ depends on PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN
help
Map driver for accessing PCMCIA linear flash memory cards. These
cards are usually around 4-16MiB in size. This does not include
@@ -591,13 +623,12 @@ config MTD_BAST_MAXSIZE
config MTD_SHARP_SL
bool "ROM mapped on Sharp SL Series"
- depends on MTD && ARCH_PXA
+ depends on ARCH_PXA
help
This enables access to the flash chip on the Sharp SL Series of PDAs.
config MTD_PLATRAM
tristate "Map driver for platform device RAM (mtd-ram)"
- depends on MTD
select MTD_RAM
help
Map driver for RAM areas described via the platform device
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 071d0bf922b6..de036c5e6139 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -27,6 +27,8 @@ obj-$(CONFIG_MTD_CEIVA) += ceiva.o
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
+obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
+obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o
obj-$(CONFIG_MTD_PNC2000) += pnc2000.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c
index 7fc8097e41d2..84fbe0e8c47e 100644
--- a/drivers/mtd/maps/alchemy-flash.c
+++ b/drivers/mtd/maps/alchemy-flash.c
@@ -1,10 +1,7 @@
/*
* Flash memory access on AMD Alchemy evaluation boards
*
- * $Id: alchemy-flash.c,v 1.2 2005/11/07 11:14:26 gleixner Exp $
- *
* (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com>
- *
*/
#include <linux/init.h>
@@ -18,12 +15,6 @@
#include <asm/io.h>
-#ifdef DEBUG_RW
-#define DBG(x...) printk(x)
-#else
-#define DBG(x...)
-#endif
-
#ifdef CONFIG_MIPS_PB1000
#define BOARD_MAP_NAME "Pb1000 Flash"
#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */
diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c
index 3d4a4d8ac789..688ef495888a 100644
--- a/drivers/mtd/maps/ck804xrom.c
+++ b/drivers/mtd/maps/ck804xrom.c
@@ -338,7 +338,7 @@ static int __init init_ck804xrom(void)
}
return -ENXIO;
#if 0
- return pci_module_init(&ck804xrom_driver);
+ return pci_register_driver(&ck804xrom_driver);
#endif
}
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c
index 2b6504ecbbd1..894c0b271289 100644
--- a/drivers/mtd/maps/plat-ram.c
+++ b/drivers/mtd/maps/plat-ram.c
@@ -169,7 +169,8 @@ static int platram_probe(struct platform_device *pdev)
goto exit_free;
}
- dev_dbg(&pdev->dev, "got platform resource %p (0x%lx)\n", res, res->start);
+ dev_dbg(&pdev->dev, "got platform resource %p (0x%llx)\n", res,
+ (unsigned long long)res->start);
/* setup map parameters */
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
new file mode 100644
index 000000000000..7e0377ec1c40
--- /dev/null
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -0,0 +1,184 @@
+/*
+ * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
+ * Config with both CFI and JEDEC device support.
+ *
+ * Basically physmap.c with the addition of partitions and
+ * an array of mapping info to accomodate more than one flash type per board.
+ *
+ * Copyright 2005-2007 PMC-Sierra, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+
+#include <msp_prom.h>
+#include <msp_regs.h>
+
+
+static struct mtd_info **msp_flash;
+static struct mtd_partition **msp_parts;
+static struct map_info *msp_maps;
+static int fcnt;
+
+#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n",__FUNCTION__,__LINE__)
+
+int __init init_msp_flash(void)
+{
+ int i, j;
+ int offset, coff;
+ char *env;
+ int pcnt;
+ char flash_name[] = "flash0";
+ char part_name[] = "flash0_0";
+ unsigned addr, size;
+
+ /* If ELB is disabled by "ful-mux" mode, we can't get at flash */
+ if ((*DEV_ID_REG & DEV_ID_SINGLE_PC) &&
+ (*ELB_1PC_EN_REG & SINGLE_PCCARD)) {
+ printk(KERN_NOTICE "Single PC Card mode: no flash access\n");
+ return -ENXIO;
+ }
+
+ /* examine the prom environment for flash devices */
+ for (fcnt = 0; (env = prom_getenv(flash_name)); fcnt++)
+ flash_name[5] = '0' + fcnt + 1;
+
+ if (fcnt < 1)
+ return -ENXIO;
+
+ printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
+ msp_flash = (struct mtd_info **)kmalloc(
+ fcnt * sizeof(struct map_info *), GFP_KERNEL);
+ msp_parts = (struct mtd_partition **)kmalloc(
+ fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
+ msp_maps = (struct map_info *)kmalloc(
+ fcnt * sizeof(struct mtd_info), GFP_KERNEL);
+ memset(msp_maps, 0, fcnt * sizeof(struct mtd_info));
+
+ /* loop over the flash devices, initializing each */
+ for (i = 0; i < fcnt; i++) {
+ /* examine the prom environment for flash partititions */
+ part_name[5] = '0' + i;
+ part_name[7] = '0';
+ for (pcnt = 0; (env = prom_getenv(part_name)); pcnt++)
+ part_name[7] = '0' + pcnt + 1;
+
+ if (pcnt == 0) {
+ printk(KERN_NOTICE "Skipping flash device %d "
+ "(no partitions defined)\n", i);
+ continue;
+ }
+
+ msp_parts[i] = (struct mtd_partition *)kmalloc(
+ pcnt * sizeof(struct mtd_partition), GFP_KERNEL);
+ memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition));
+
+ /* now initialize the devices proper */
+ flash_name[5] = '0' + i;
+ env = prom_getenv(flash_name);
+
+ if (sscanf(env, "%x:%x", &addr, &size) < 2)
+ return -ENXIO;
+ addr = CPHYSADDR(addr);
+
+ printk(KERN_NOTICE
+ "MSP flash device \"%s\": 0x%08x at 0x%08x\n",
+ flash_name, size, addr);
+ /* This must matchs the actual size of the flash chip */
+ msp_maps[i].size = size;
+ msp_maps[i].phys = addr;
+
+ /*
+ * Platforms have a specific limit of the size of memory
+ * which may be mapped for flash:
+ */
+ if (size > CONFIG_MSP_FLASH_MAP_LIMIT)
+ size = CONFIG_MSP_FLASH_MAP_LIMIT;
+ msp_maps[i].virt = ioremap(addr, size);
+ msp_maps[i].bankwidth = 1;
+ msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL),
+ flash_name, 7);
+
+ if (msp_maps[i].virt == NULL)
+ return -ENXIO;
+
+ for (j = 0; j < pcnt; j++) {
+ part_name[5] = '0' + i;
+ part_name[7] = '0' + j;
+
+ env = prom_getenv(part_name);
+
+ if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2)
+ return -ENXIO;
+
+ msp_parts[i][j].size = size;
+ msp_parts[i][j].offset = offset;
+ msp_parts[i][j].name = env + coff;
+ }
+
+ /* now probe and add the device */
+ simple_map_init(&msp_maps[i]);
+ msp_flash[i] = do_map_probe("cfi_probe", &msp_maps[i]);
+ if (msp_flash[i]) {
+ msp_flash[i]->owner = THIS_MODULE;
+ add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt);
+ } else {
+ printk(KERN_ERR "map probe failed for flash\n");
+ return -ENXIO;
+ }
+ }
+
+ return 0;
+}
+
+static void __exit cleanup_msp_flash(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) {
+ del_mtd_partitions(msp_flash[i]);
+ map_destroy(msp_flash[i]);
+ iounmap((void *)msp_maps[i].virt);
+
+ /* free the memory */
+ kfree(msp_maps[i].name);
+ kfree(msp_parts[i]);
+ }
+
+ kfree(msp_flash);
+ kfree(msp_parts);
+ kfree(msp_maps);
+}
+
+MODULE_AUTHOR("PMC-Sierra, Inc");
+MODULE_DESCRIPTION("MTD map driver for PMC-Sierra MSP boards");
+MODULE_LICENSE("GPL");
+
+module_init(init_msp_flash);
+module_exit(cleanup_msp_flash);
diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c
new file mode 100644
index 000000000000..18049bceba8d
--- /dev/null
+++ b/drivers/mtd/maps/pmcmsp-ramroot.c
@@ -0,0 +1,105 @@
+/*
+ * Mapping of the rootfs in a physical region of memory
+ *
+ * Copyright (C) 2005-2007 PMC-Sierra Inc.
+ * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.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 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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/root_dev.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+#include <asm/io.h>
+
+#include <msp_prom.h>
+
+static struct mtd_info *rr_mtd;
+
+struct map_info rr_map = {
+ .name = "ramroot",
+ .bankwidth = 4,
+};
+
+static int __init init_rrmap(void)
+{
+ void *ramroot_start;
+ unsigned long ramroot_size;
+
+ /* Check for supported rootfs types */
+ if (get_ramroot(&ramroot_start, &ramroot_size)) {
+ rr_map.phys = CPHYSADDR(ramroot_start);
+ rr_map.size = ramroot_size;
+
+ printk(KERN_NOTICE
+ "PMC embedded root device: 0x%08lx @ 0x%08lx\n",
+ rr_map.size, (unsigned long)rr_map.phys);
+ } else {
+ printk(KERN_ERR
+ "init_rrmap: no supported embedded rootfs detected!\n");
+ return -ENXIO;
+ }
+
+ /* Map rootfs to I/O space for block device driver */
+ rr_map.virt = ioremap(rr_map.phys, rr_map.size);
+ if (!rr_map.virt) {
+ printk(KERN_ERR "Failed to ioremap\n");
+ return -EIO;
+ }
+
+ simple_map_init(&rr_map);
+
+ rr_mtd = do_map_probe("map_ram", &rr_map);
+ if (rr_mtd) {
+ rr_mtd->owner = THIS_MODULE;
+
+ add_mtd_device(rr_mtd);
+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index);
+
+ return 0;
+ }
+
+ iounmap(rr_map.virt);
+ return -ENXIO;
+}
+
+static void __exit cleanup_rrmap(void)
+{
+ del_mtd_device(rr_mtd);
+ map_destroy(rr_mtd);
+
+ iounmap(rr_map.virt);
+ rr_map.virt = NULL;
+}
+
+MODULE_AUTHOR("PMC-Sierra, Inc");
+MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem");
+MODULE_LICENSE("GPL");
+
+module_init(init_rrmap);
+module_exit(cleanup_rrmap);
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 4db2055cee31..001af7f7ddda 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -39,7 +39,7 @@ MODULE_VERSION("2.0");
static LIST_HEAD(device_list);
struct uflash_dev {
- char *name; /* device name */
+ const char *name; /* device name */
struct map_info map; /* mtd map info */
struct mtd_info *mtd; /* mtd info */
};
@@ -80,7 +80,7 @@ int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp)
up->name = of_get_property(dp, "model", NULL);
if (up->name && 0 < strlen(up->name))
- up->map.name = up->name;
+ up->map.name = (char *)up->name;
up->map.phys = res->start;
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index b879a66daa9e..524b83b5ebf5 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -20,6 +20,7 @@
#include <linux/hdreg.h>
#include <linux/init.h>
#include <linux/mutex.h>
+#include <linux/kthread.h>
#include <asm/uaccess.h>
static LIST_HEAD(blktrans_majors);
@@ -28,9 +29,7 @@ extern struct mutex mtd_table_mutex;
extern struct mtd_info *mtd_table[];
struct mtd_blkcore_priv {
- struct completion thread_dead;
- int exiting;
- wait_queue_head_t thread_wq;
+ struct task_struct *thread;
struct request_queue *rq;
spinlock_t queue_lock;
};
@@ -83,38 +82,19 @@ static int mtd_blktrans_thread(void *arg)
/* we might get involved when memory gets low, so use PF_MEMALLOC */
current->flags |= PF_MEMALLOC | PF_NOFREEZE;
- daemonize("%sd", tr->name);
-
- /* daemonize() doesn't do this for us since some kernel threads
- actually want to deal with signals. We can't just call
- exit_sighand() since that'll cause an oops when we finally
- do exit. */
- spin_lock_irq(&current->sighand->siglock);
- sigfillset(&current->blocked);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
spin_lock_irq(rq->queue_lock);
-
- while (!tr->blkcore_priv->exiting) {
+ while (!kthread_should_stop()) {
struct request *req;
struct mtd_blktrans_dev *dev;
int res = 0;
- DECLARE_WAITQUEUE(wait, current);
req = elv_next_request(rq);
if (!req) {
- add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
-
spin_unlock_irq(rq->queue_lock);
-
schedule();
- remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
-
spin_lock_irq(rq->queue_lock);
-
continue;
}
@@ -133,13 +113,13 @@ static int mtd_blktrans_thread(void *arg)
}
spin_unlock_irq(rq->queue_lock);
- complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
+ return 0;
}
static void mtd_blktrans_request(struct request_queue *rq)
{
struct mtd_blktrans_ops *tr = rq->queuedata;
- wake_up(&tr->blkcore_priv->thread_wq);
+ wake_up_process(tr->blkcore_priv->thread);
}
@@ -388,8 +368,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
return ret;
}
spin_lock_init(&tr->blkcore_priv->queue_lock);
- init_completion(&tr->blkcore_priv->thread_dead);
- init_waitqueue_head(&tr->blkcore_priv->thread_wq);
tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
if (!tr->blkcore_priv->rq) {
@@ -403,13 +381,14 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
tr->blkshift = ffs(tr->blksize) - 1;
- ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL);
- if (ret < 0) {
+ tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
+ "%sd", tr->name);
+ if (IS_ERR(tr->blkcore_priv->thread)) {
blk_cleanup_queue(tr->blkcore_priv->rq);
unregister_blkdev(tr->major, tr->name);
kfree(tr->blkcore_priv);
mutex_unlock(&mtd_table_mutex);
- return ret;
+ return PTR_ERR(tr->blkcore_priv->thread);
}
INIT_LIST_HEAD(&tr->devs);
@@ -432,9 +411,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
mutex_lock(&mtd_table_mutex);
/* Clean up the kernel thread */
- tr->blkcore_priv->exiting = 1;
- wake_up(&tr->blkcore_priv->thread_wq);
- wait_for_completion(&tr->blkcore_priv->thread_dead);
+ kthread_stop(tr->blkcore_priv->thread);
/* Remove it from the list of active majors */
list_del(&tr->list);
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 1592eac64e57..8c86b802f212 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -553,7 +553,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
- if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
+ if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
return -EINVAL;
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2d12dcdd740c..d05873b8c155 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,10 +1,7 @@
# drivers/mtd/nand/Kconfig
# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $
-menu "NAND Flash Device Drivers"
- depends on MTD!=n
-
-config MTD_NAND
+menuconfig MTD_NAND
tristate "NAND Device Support"
depends on MTD
select MTD_NAND_IDS
@@ -13,9 +10,10 @@ config MTD_NAND
devices. For further information see
<http://www.linux-mtd.infradead.org/doc/nand.html>.
+if MTD_NAND
+
config MTD_NAND_VERIFY_WRITE
bool "Verify NAND page writes"
- depends on MTD_NAND
help
This adds an extra check when data is written to the flash. The
NAND flash device internally checks only bits transitioning
@@ -25,53 +23,61 @@ config MTD_NAND_VERIFY_WRITE
config MTD_NAND_ECC_SMC
bool "NAND ECC Smart Media byte order"
- depends on MTD_NAND
default n
help
Software ECC according to the Smart Media Specification.
The original Linux implementation had byte 0 and 1 swapped.
+config MTD_NAND_MUSEUM_IDS
+ bool "Enable chip ids for obsolete ancient NAND devices"
+ depends on MTD_NAND
+ default n
+ help
+ Enable this option only when your board has first generation
+ NAND chips (page size 256 byte, erase size 4-8KiB). The IDs
+ of these chips were reused by later, larger chips.
+
config MTD_NAND_AUTCPU12
tristate "SmartMediaCard on autronix autcpu12 board"
- depends on MTD_NAND && ARCH_AUTCPU12
+ depends on ARCH_AUTCPU12
help
This enables the driver for the autronix autcpu12 board to
access the SmartMediaCard.
config MTD_NAND_EDB7312
tristate "Support for Cirrus Logic EBD7312 evaluation board"
- depends on MTD_NAND && ARCH_EDB7312
+ depends on ARCH_EDB7312
help
This enables the driver for the Cirrus Logic EBD7312 evaluation
board to access the onboard NAND Flash.
config MTD_NAND_H1900
tristate "iPAQ H1900 flash"
- depends on MTD_NAND && ARCH_PXA && MTD_PARTITIONS
+ depends on ARCH_PXA && MTD_PARTITIONS
help
This enables the driver for the iPAQ h1900 flash.
config MTD_NAND_SPIA
tristate "NAND Flash device on SPIA board"
- depends on ARCH_P720T && MTD_NAND
+ depends on ARCH_P720T
help
If you had to ask, you don't have one. Say 'N'.
config MTD_NAND_AMS_DELTA
tristate "NAND Flash device on Amstrad E3"
- depends on MACH_AMS_DELTA && MTD_NAND
+ depends on MACH_AMS_DELTA
help
Support for NAND flash on Amstrad E3 (Delta).
config MTD_NAND_TOTO
tristate "NAND Flash device on TOTO board"
- depends on ARCH_OMAP && MTD_NAND && BROKEN
+ depends on ARCH_OMAP && BROKEN
help
Support for NAND flash on Texas Instruments Toto platform.
config MTD_NAND_TS7250
tristate "NAND Flash device on TS-7250 board"
- depends on MACH_TS72XX && MTD_NAND
+ depends on MACH_TS72XX
help
Support for NAND flash on Technologic Systems TS-7250 platform.
@@ -80,14 +86,14 @@ config MTD_NAND_IDS
config MTD_NAND_AU1550
tristate "Au1550/1200 NAND support"
- depends on (SOC_AU1200 || SOC_AU1550) && MTD_NAND
+ depends on SOC_AU1200 || SOC_AU1550
help
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
config MTD_NAND_RTC_FROM4
tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
- depends on MTD_NAND && SH_SOLUTION_ENGINE
+ depends on SH_SOLUTION_ENGINE
select REED_SOLOMON
select REED_SOLOMON_DEC8
select BITREVERSE
@@ -97,13 +103,13 @@ config MTD_NAND_RTC_FROM4
config MTD_NAND_PPCHAMELEONEVB
tristate "NAND Flash device on PPChameleonEVB board"
- depends on PPCHAMELEONEVB && MTD_NAND && BROKEN
+ depends on PPCHAMELEONEVB && BROKEN
help
This enables the NAND flash driver on the PPChameleon EVB Board.
config MTD_NAND_S3C2410
tristate "NAND Flash support for S3C2410/S3C2440 SoC"
- depends on ARCH_S3C2410 && MTD_NAND
+ depends on ARCH_S3C2410
help
This enables the NAND flash controller on the S3C2410 and S3C2440
SoCs
@@ -128,7 +134,7 @@ config MTD_NAND_S3C2410_HWECC
config MTD_NAND_NDFC
tristate "NDFC NanD Flash Controller"
- depends on MTD_NAND && 44x
+ depends on 44x
select MTD_NAND_ECC_SMC
help
NDFC Nand Flash Controllers are integrated in EP44x SoCs
@@ -145,7 +151,7 @@ config MTD_NAND_S3C2410_CLKSTOP
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
- depends on MTD_NAND && EXPERIMENTAL
+ depends on EXPERIMENTAL
select REED_SOLOMON
select REED_SOLOMON_DEC16
help
@@ -215,11 +221,11 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
- depends on MTD_NAND && ARCH_PXA
+ depends on ARCH_PXA
config MTD_NAND_BASLER_EXCITE
tristate "Support for NAND Flash on Basler eXcite"
- depends on MTD_NAND && BASLER_EXCITE
+ depends on BASLER_EXCITE
help
This enables the driver for the NAND flash device found on the
Basler eXcite Smart Camera. If built as a module, the driver
@@ -227,14 +233,14 @@ config MTD_NAND_BASLER_EXCITE
config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip"
- depends on MTD_NAND && PCI
+ depends on PCI
help
Use NAND flash attached to the CAFÉ chip designed for the $100
laptop.
config MTD_NAND_CS553X
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
- depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH)
+ depends on X86_32 && (X86_PC || X86_GENERICARCH)
help
The CS553x companion chips for the AMD Geode processor
include NAND flash controllers with built-in hardware ECC
@@ -247,16 +253,21 @@ config MTD_NAND_CS553X
config MTD_NAND_AT91
bool "Support for NAND Flash / SmartMedia on AT91"
- depends on MTD_NAND && ARCH_AT91
+ depends on ARCH_AT91
help
Enables support for NAND Flash / Smart Media Card interface
on Atmel AT91 processors.
+config MTD_NAND_CM_X270
+ tristate "Support for NAND Flash on CM-X270 modules"
+ depends on MTD_NAND && MACH_ARMCORE
+
+
config MTD_NAND_NANDSIM
tristate "Support for NAND Flash Simulator"
- depends on MTD_NAND && MTD_PARTITIONS
+ depends on MTD_PARTITIONS
help
The simulator may simulate various NAND flash chips for the
MTD nand layer.
-endmenu
+endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 80f1dfc77949..6872031a3fb2 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
+obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c
index fd6bb3ed40df..c328a7514510 100644
--- a/drivers/mtd/nand/cafe.c
+++ b/drivers/mtd/nand/cafe.c
@@ -530,7 +530,6 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
{
struct mtd_info *mtd;
struct cafe_priv *cafe;
- uint32_t timing1, timing2, timing3;
uint32_t ctrl;
int err = 0;
@@ -587,21 +586,19 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
}
if (numtimings == 3) {
- timing1 = timing[0];
- timing2 = timing[1];
- timing3 = timing[2];
cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
- timing1, timing2, timing3);
+ timing[0], timing[1], timing[2]);
} else {
- timing1 = cafe_readl(cafe, NAND_TIMING1);
- timing2 = cafe_readl(cafe, NAND_TIMING2);
- timing3 = cafe_readl(cafe, NAND_TIMING3);
+ timing[0] = cafe_readl(cafe, NAND_TIMING1);
+ timing[1] = cafe_readl(cafe, NAND_TIMING2);
+ timing[2] = cafe_readl(cafe, NAND_TIMING3);
- if (timing1 | timing2 | timing3) {
- cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3);
+ if (timing[0] | timing[1] | timing[2]) {
+ cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n",
+ timing[0], timing[1], timing[2]);
} else {
dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
- timing1 = timing2 = timing3 = 0xffffffff;
+ timing[0] = timing[1] = timing[2] = 0xffffffff;
}
}
@@ -609,9 +606,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe_writel(cafe, 1, NAND_RESET);
cafe_writel(cafe, 0, NAND_RESET);
- cafe_writel(cafe, timing1, NAND_TIMING1);
- cafe_writel(cafe, timing2, NAND_TIMING2);
- cafe_writel(cafe, timing3, NAND_TIMING3);
+ cafe_writel(cafe, timing[0], NAND_TIMING1);
+ cafe_writel(cafe, timing[1], NAND_TIMING2);
+ cafe_writel(cafe, timing[2], NAND_TIMING3);
cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
new file mode 100644
index 000000000000..cb663ef245d5
--- /dev/null
+++ b/drivers/mtd/nand/cmx270_nand.c
@@ -0,0 +1,267 @@
+/*
+ * linux/drivers/mtd/nand/cmx270-nand.c
+ *
+ * Copyright (C) 2006 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Derived from drivers/mtd/nand/h1910.c
+ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ *
+ * 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.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device found on the
+ * CM-X270 board.
+ */
+
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#define GPIO_NAND_CS (11)
+#define GPIO_NAND_RB (89)
+
+/* This macro needed to ensure in-order operation of GPIO and local
+ * bus. Without both asm command and dummy uncached read there're
+ * states when NAND access is broken. I've looked for such macro(s) in
+ * include/asm-arm but found nothing approptiate.
+ * dmac_clean_range is close, but is makes cache invalidation
+ * unnecessary here and it cannot be used in module
+ */
+#define DRAIN_WB() \
+ do { \
+ unsigned char dummy; \
+ asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); \
+ dummy=*((unsigned char*)UNCACHED_ADDR); \
+ } while(0)
+
+/* MTD structure for CM-X270 board */
+static struct mtd_info *cmx270_nand_mtd;
+
+/* remaped IO address of the device */
+static void __iomem *cmx270_nand_io;
+
+/*
+ * Define static partitions for flash device
+ */
+static struct mtd_partition partition_info[] = {
+ [0] = {
+ .name = "cmx270-0",
+ .offset = 0,
+ .size = MTDPART_SIZ_FULL
+ }
+};
+#define NUM_PARTITIONS (ARRAY_SIZE(partition_info))
+
+const char *part_probes[] = { "cmdlinepart", NULL };
+
+static u_char cmx270_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ return (readl(this->IO_ADDR_R) >> 16);
+}
+
+static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ writel((*buf++ << 16), this->IO_ADDR_W);
+}
+
+static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ *buf++ = readl(this->IO_ADDR_R) >> 16;
+}
+
+static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ if (buf[i] != (u_char)(readl(this->IO_ADDR_R) >> 16))
+ return -EFAULT;
+
+ return 0;
+}
+
+static inline void nand_cs_on(void)
+{
+ GPCR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
+}
+
+static void nand_cs_off(void)
+{
+ DRAIN_WB();
+
+ GPSR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
+}
+
+/*
+ * hardware specific access to control-lines
+ */
+static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ struct nand_chip* this = mtd->priv;
+ unsigned int nandaddr = (unsigned int)this->IO_ADDR_W;
+
+ DRAIN_WB();
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if ( ctrl & NAND_ALE )
+ nandaddr |= (1 << 3);
+ else
+ nandaddr &= ~(1 << 3);
+ if ( ctrl & NAND_CLE )
+ nandaddr |= (1 << 2);
+ else
+ nandaddr &= ~(1 << 2);
+ if ( ctrl & NAND_NCE )
+ nand_cs_on();
+ else
+ nand_cs_off();
+ }
+
+ DRAIN_WB();
+ this->IO_ADDR_W = (void __iomem*)nandaddr;
+ if (dat != NAND_CMD_NONE)
+ writel((dat << 16), this->IO_ADDR_W);
+
+ DRAIN_WB();
+}
+
+/*
+ * read device ready pin
+ */
+static int cmx270_device_ready(struct mtd_info *mtd)
+{
+ DRAIN_WB();
+
+ return (GPLR(GPIO_NAND_RB) & GPIO_bit(GPIO_NAND_RB));
+}
+
+/*
+ * Main initialization routine
+ */
+static int cmx270_init(void)
+{
+ struct nand_chip *this;
+ const char *part_type;
+ struct mtd_partition *mtd_parts;
+ int mtd_parts_nb = 0;
+ int ret;
+
+ /* Allocate memory for MTD device structure and private data */
+ cmx270_nand_mtd = kzalloc(sizeof(struct mtd_info) +
+ sizeof(struct nand_chip),
+ GFP_KERNEL);
+ if (!cmx270_nand_mtd) {
+ printk("Unable to allocate CM-X270 NAND MTD device structure.\n");
+ return -ENOMEM;
+ }
+
+ cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12);
+ if (!cmx270_nand_io) {
+ printk("Unable to ioremap NAND device\n");
+ ret = -EINVAL;
+ goto err1;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *)(&cmx270_nand_mtd[1]);
+
+ /* Link the private data with the MTD structure */
+ cmx270_nand_mtd->owner = THIS_MODULE;
+ cmx270_nand_mtd->priv = this;
+
+ /* insert callbacks */
+ this->IO_ADDR_R = cmx270_nand_io;
+ this->IO_ADDR_W = cmx270_nand_io;
+ this->cmd_ctrl = cmx270_hwcontrol;
+ this->dev_ready = cmx270_device_ready;
+
+ /* 15 us command delay time */
+ this->chip_delay = 20;
+ this->ecc.mode = NAND_ECC_SOFT;
+
+ /* read/write functions */
+ this->read_byte = cmx270_read_byte;
+ this->read_buf = cmx270_read_buf;
+ this->write_buf = cmx270_write_buf;
+ this->verify_buf = cmx270_verify_buf;
+
+ /* Scan to find existence of the device */
+ if (nand_scan (cmx270_nand_mtd, 1)) {
+ printk(KERN_NOTICE "No NAND device\n");
+ ret = -ENXIO;
+ goto err2;
+ }
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ mtd_parts_nb = parse_mtd_partitions(cmx270_nand_mtd, part_probes,
+ &mtd_parts, 0);
+ if (mtd_parts_nb > 0)
+ part_type = "command line";
+ else
+ mtd_parts_nb = 0;
+#endif
+ if (!mtd_parts_nb) {
+ mtd_parts = partition_info;
+ mtd_parts_nb = NUM_PARTITIONS;
+ part_type = "static";
+ }
+
+ /* Register the partitions */
+ printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+ ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb);
+ if (ret)
+ goto err2;
+
+ /* Return happy */
+ return 0;
+
+err2:
+ iounmap(cmx270_nand_io);
+err1:
+ kfree(cmx270_nand_mtd);
+
+ return ret;
+
+}
+module_init(cmx270_init);
+
+/*
+ * Clean up routine
+ */
+static void cmx270_cleanup(void)
+{
+ /* Release resources, unregister device */
+ nand_release(cmx270_nand_mtd);
+
+ iounmap(cmx270_nand_io);
+
+ /* Free the MTD device structure */
+ kfree (cmx270_nand_mtd);
+}
+module_exit(cmx270_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module");
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 6af37b8cff65..04de315e4937 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -312,7 +312,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
/* Select the NAND device */
chip->select_chip(mtd, chipnr);
} else
- page = (int)ofs;
+ page = (int)(ofs >> chip->page_shift);
if (chip->options & NAND_BUSWIDTH_16) {
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
@@ -350,7 +350,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
int block, ret;
/* Get block number */
- block = ((int)ofs) >> chip->bbt_erase_shift;
+ block = (int)(ofs >> chip->bbt_erase_shift);
if (chip->bbt)
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
@@ -771,7 +771,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *ecc_code = chip->buffers->ecccode;
int *eccpos = chip->ecc.layout->eccpos;
- nand_read_page_raw(mtd, chip, buf);
+ chip->ecc.read_page_raw(mtd, chip, buf);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
@@ -1426,7 +1426,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- nand_write_page_raw(mtd, chip, buf);
+ chip->ecc.write_page_raw(mtd, chip, buf);
}
/**
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 2e2cdf2fc91d..2fc674a190cf 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -24,6 +24,8 @@
* 512 512 Byte page size
*/
struct nand_flash_dev nand_flash_ids[] = {
+
+#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
@@ -39,6 +41,7 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+#endif
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
@@ -137,6 +140,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
+ {NAND_MFR_MICRON, "Micron"},
{0x0, "Unknown"}
};
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index c3bca9590ad2..205df0f771fe 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -37,6 +37,8 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/random.h>
/* Default simulator parameters values */
#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \
@@ -90,6 +92,15 @@ static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH;
static uint do_delays = CONFIG_NANDSIM_DO_DELAYS;
static uint log = CONFIG_NANDSIM_LOG;
static uint dbg = CONFIG_NANDSIM_DBG;
+static unsigned long parts[MAX_MTD_DEVICES];
+static unsigned int parts_num;
+static char *badblocks = NULL;
+static char *weakblocks = NULL;
+static char *weakpages = NULL;
+static unsigned int bitflips = 0;
+static char *gravepages = NULL;
+static unsigned int rptwear = 0;
+static unsigned int overridesize = 0;
module_param(first_id_byte, uint, 0400);
module_param(second_id_byte, uint, 0400);
@@ -104,8 +115,16 @@ module_param(bus_width, uint, 0400);
module_param(do_delays, uint, 0400);
module_param(log, uint, 0400);
module_param(dbg, uint, 0400);
-
-MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)");
+module_param_array(parts, ulong, &parts_num, 0400);
+module_param(badblocks, charp, 0400);
+module_param(weakblocks, charp, 0400);
+module_param(weakpages, charp, 0400);
+module_param(bitflips, uint, 0400);
+module_param(gravepages, charp, 0400);
+module_param(rptwear, uint, 0400);
+module_param(overridesize, uint, 0400);
+
+MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command");
MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
@@ -118,6 +137,23 @@ MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)");
MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero");
MODULE_PARM_DESC(log, "Perform logging if not zero");
MODULE_PARM_DESC(dbg, "Output debug information if not zero");
+MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas");
+/* Page and erase block positions for the following parameters are independent of any partitions */
+MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas");
+MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]"
+ " separated by commas e.g. 113:2 means eb 113"
+ " can be erased only twice before failing");
+MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]"
+ " separated by commas e.g. 1401:2 means page 1401"
+ " can be written only twice before failing");
+MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)");
+MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]"
+ " separated by commas e.g. 1401:2 means page 1401"
+ " can be read only twice before failing");
+MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero");
+MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. "
+ "The size is specified in erase blocks and as the exponent of a power of two"
+ " e.g. 5 means a size of 32 erase blocks");
/* The largest possible page size */
#define NS_LARGEST_PAGE_SIZE 2048
@@ -131,9 +167,11 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero");
#define NS_DBG(args...) \
do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
#define NS_WARN(args...) \
- do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0)
+ do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0)
#define NS_ERR(args...) \
- do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0)
+ do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0)
+#define NS_INFO(args...) \
+ do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0)
/* Busy-wait delay macros (microseconds, milliseconds) */
#define NS_UDELAY(us) \
@@ -238,7 +276,8 @@ union ns_mem {
* The structure which describes all the internal simulator data.
*/
struct nandsim {
- struct mtd_partition part;
+ struct mtd_partition partitions[MAX_MTD_DEVICES];
+ unsigned int nbparts;
uint busw; /* flash chip bus width (8 or 16) */
u_char ids[4]; /* chip's ID bytes */
@@ -338,6 +377,38 @@ static struct nandsim_operations {
STATE_DATAOUT, STATE_READY}}
};
+struct weak_block {
+ struct list_head list;
+ unsigned int erase_block_no;
+ unsigned int max_erases;
+ unsigned int erases_done;
+};
+
+static LIST_HEAD(weak_blocks);
+
+struct weak_page {
+ struct list_head list;
+ unsigned int page_no;
+ unsigned int max_writes;
+ unsigned int writes_done;
+};
+
+static LIST_HEAD(weak_pages);
+
+struct grave_page {
+ struct list_head list;
+ unsigned int page_no;
+ unsigned int max_reads;
+ unsigned int reads_done;
+};
+
+static LIST_HEAD(grave_pages);
+
+static unsigned long *erase_block_wear = NULL;
+static unsigned int wear_eb_count = 0;
+static unsigned long total_wear = 0;
+static unsigned int rptwear_cnt = 0;
+
/* MTD structure for NAND controller */
static struct mtd_info *nsmtd;
@@ -381,6 +452,13 @@ static void free_device(struct nandsim *ns)
}
}
+static char *get_partition_name(int i)
+{
+ char buf[64];
+ sprintf(buf, "NAND simulator partition %d", i);
+ return kstrdup(buf, GFP_KERNEL);
+}
+
/*
* Initialize the nandsim structure.
*
@@ -390,7 +468,9 @@ static int init_nandsim(struct mtd_info *mtd)
{
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
struct nandsim *ns = (struct nandsim *)(chip->priv);
- int i;
+ int i, ret = 0;
+ u_int32_t remains;
+ u_int32_t next_offset;
if (NS_IS_INITIALIZED(ns)) {
NS_ERR("init_nandsim: nandsim is already initialized\n");
@@ -448,6 +528,40 @@ static int init_nandsim(struct mtd_info *mtd)
}
}
+ /* Fill the partition_info structure */
+ if (parts_num > ARRAY_SIZE(ns->partitions)) {
+ NS_ERR("too many partitions.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ remains = ns->geom.totsz;
+ next_offset = 0;
+ for (i = 0; i < parts_num; ++i) {
+ unsigned long part = parts[i];
+ if (!part || part > remains / ns->geom.secsz) {
+ NS_ERR("bad partition size.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ ns->partitions[i].name = get_partition_name(i);
+ ns->partitions[i].offset = next_offset;
+ ns->partitions[i].size = part * ns->geom.secsz;
+ next_offset += ns->partitions[i].size;
+ remains -= ns->partitions[i].size;
+ }
+ ns->nbparts = parts_num;
+ if (remains) {
+ if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
+ NS_ERR("too many partitions.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ ns->partitions[i].name = get_partition_name(i);
+ ns->partitions[i].offset = next_offset;
+ ns->partitions[i].size = remains;
+ ns->nbparts += 1;
+ }
+
/* Detect how many ID bytes the NAND chip outputs */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (second_id_byte != nand_flash_ids[i].id)
@@ -474,7 +588,7 @@ static int init_nandsim(struct mtd_info *mtd)
printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
printk("options: %#x\n", ns->options);
- if (alloc_device(ns) != 0)
+ if ((ret = alloc_device(ns)) != 0)
goto error;
/* Allocate / initialize the internal buffer */
@@ -482,21 +596,17 @@ static int init_nandsim(struct mtd_info *mtd)
if (!ns->buf.byte) {
NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
ns->geom.pgszoob);
+ ret = -ENOMEM;
goto error;
}
memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
- /* Fill the partition_info structure */
- ns->part.name = "NAND simulator partition";
- ns->part.offset = 0;
- ns->part.size = ns->geom.totsz;
-
return 0;
error:
free_device(ns);
- return -ENOMEM;
+ return ret;
}
/*
@@ -510,6 +620,287 @@ static void free_nandsim(struct nandsim *ns)
return;
}
+static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd)
+{
+ char *w;
+ int zero_ok;
+ unsigned int erase_block_no;
+ loff_t offset;
+
+ if (!badblocks)
+ return 0;
+ w = badblocks;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ erase_block_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !erase_block_no) {
+ NS_ERR("invalid badblocks.\n");
+ return -EINVAL;
+ }
+ offset = erase_block_no * ns->geom.secsz;
+ if (mtd->block_markbad(mtd, offset)) {
+ NS_ERR("invalid badblocks.\n");
+ return -EINVAL;
+ }
+ if (*w == ',')
+ w += 1;
+ } while (*w);
+ return 0;
+}
+
+static int parse_weakblocks(void)
+{
+ char *w;
+ int zero_ok;
+ unsigned int erase_block_no;
+ unsigned int max_erases;
+ struct weak_block *wb;
+
+ if (!weakblocks)
+ return 0;
+ w = weakblocks;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ erase_block_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !erase_block_no) {
+ NS_ERR("invalid weakblocks.\n");
+ return -EINVAL;
+ }
+ max_erases = 3;
+ if (*w == ':') {
+ w += 1;
+ max_erases = simple_strtoul(w, &w, 0);
+ }
+ if (*w == ',')
+ w += 1;
+ wb = kzalloc(sizeof(*wb), GFP_KERNEL);
+ if (!wb) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ wb->erase_block_no = erase_block_no;
+ wb->max_erases = max_erases;
+ list_add(&wb->list, &weak_blocks);
+ } while (*w);
+ return 0;
+}
+
+static int erase_error(unsigned int erase_block_no)
+{
+ struct weak_block *wb;
+
+ list_for_each_entry(wb, &weak_blocks, list)
+ if (wb->erase_block_no == erase_block_no) {
+ if (wb->erases_done >= wb->max_erases)
+ return 1;
+ wb->erases_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int parse_weakpages(void)
+{
+ char *w;
+ int zero_ok;
+ unsigned int page_no;
+ unsigned int max_writes;
+ struct weak_page *wp;
+
+ if (!weakpages)
+ return 0;
+ w = weakpages;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ page_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !page_no) {
+ NS_ERR("invalid weakpagess.\n");
+ return -EINVAL;
+ }
+ max_writes = 3;
+ if (*w == ':') {
+ w += 1;
+ max_writes = simple_strtoul(w, &w, 0);
+ }
+ if (*w == ',')
+ w += 1;
+ wp = kzalloc(sizeof(*wp), GFP_KERNEL);
+ if (!wp) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ wp->page_no = page_no;
+ wp->max_writes = max_writes;
+ list_add(&wp->list, &weak_pages);
+ } while (*w);
+ return 0;
+}
+
+static int write_error(unsigned int page_no)
+{
+ struct weak_page *wp;
+
+ list_for_each_entry(wp, &weak_pages, list)
+ if (wp->page_no == page_no) {
+ if (wp->writes_done >= wp->max_writes)
+ return 1;
+ wp->writes_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int parse_gravepages(void)
+{
+ char *g;
+ int zero_ok;
+ unsigned int page_no;
+ unsigned int max_reads;
+ struct grave_page *gp;
+
+ if (!gravepages)
+ return 0;
+ g = gravepages;
+ do {
+ zero_ok = (*g == '0' ? 1 : 0);
+ page_no = simple_strtoul(g, &g, 0);
+ if (!zero_ok && !page_no) {
+ NS_ERR("invalid gravepagess.\n");
+ return -EINVAL;
+ }
+ max_reads = 3;
+ if (*g == ':') {
+ g += 1;
+ max_reads = simple_strtoul(g, &g, 0);
+ }
+ if (*g == ',')
+ g += 1;
+ gp = kzalloc(sizeof(*gp), GFP_KERNEL);
+ if (!gp) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ gp->page_no = page_no;
+ gp->max_reads = max_reads;
+ list_add(&gp->list, &grave_pages);
+ } while (*g);
+ return 0;
+}
+
+static int read_error(unsigned int page_no)
+{
+ struct grave_page *gp;
+
+ list_for_each_entry(gp, &grave_pages, list)
+ if (gp->page_no == page_no) {
+ if (gp->reads_done >= gp->max_reads)
+ return 1;
+ gp->reads_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static void free_lists(void)
+{
+ struct list_head *pos, *n;
+ list_for_each_safe(pos, n, &weak_blocks) {
+ list_del(pos);
+ kfree(list_entry(pos, struct weak_block, list));
+ }
+ list_for_each_safe(pos, n, &weak_pages) {
+ list_del(pos);
+ kfree(list_entry(pos, struct weak_page, list));
+ }
+ list_for_each_safe(pos, n, &grave_pages) {
+ list_del(pos);
+ kfree(list_entry(pos, struct grave_page, list));
+ }
+ kfree(erase_block_wear);
+}
+
+static int setup_wear_reporting(struct mtd_info *mtd)
+{
+ size_t mem;
+
+ if (!rptwear)
+ return 0;
+ wear_eb_count = mtd->size / mtd->erasesize;
+ mem = wear_eb_count * sizeof(unsigned long);
+ if (mem / sizeof(unsigned long) != wear_eb_count) {
+ NS_ERR("Too many erase blocks for wear reporting\n");
+ return -ENOMEM;
+ }
+ erase_block_wear = kzalloc(mem, GFP_KERNEL);
+ if (!erase_block_wear) {
+ NS_ERR("Too many erase blocks for wear reporting\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void update_wear(unsigned int erase_block_no)
+{
+ unsigned long wmin = -1, wmax = 0, avg;
+ unsigned long deciles[10], decile_max[10], tot = 0;
+ unsigned int i;
+
+ if (!erase_block_wear)
+ return;
+ total_wear += 1;
+ if (total_wear == 0)
+ NS_ERR("Erase counter total overflow\n");
+ erase_block_wear[erase_block_no] += 1;
+ if (erase_block_wear[erase_block_no] == 0)
+ NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);
+ rptwear_cnt += 1;
+ if (rptwear_cnt < rptwear)
+ return;
+ rptwear_cnt = 0;
+ /* Calc wear stats */
+ for (i = 0; i < wear_eb_count; ++i) {
+ unsigned long wear = erase_block_wear[i];
+ if (wear < wmin)
+ wmin = wear;
+ if (wear > wmax)
+ wmax = wear;
+ tot += wear;
+ }
+ for (i = 0; i < 9; ++i) {
+ deciles[i] = 0;
+ decile_max[i] = (wmax * (i + 1) + 5) / 10;
+ }
+ deciles[9] = 0;
+ decile_max[9] = wmax;
+ for (i = 0; i < wear_eb_count; ++i) {
+ int d;
+ unsigned long wear = erase_block_wear[i];
+ for (d = 0; d < 10; ++d)
+ if (wear <= decile_max[d]) {
+ deciles[d] += 1;
+ break;
+ }
+ }
+ avg = tot / wear_eb_count;
+ /* Output wear report */
+ NS_INFO("*** Wear Report ***\n");
+ NS_INFO("Total numbers of erases: %lu\n", tot);
+ NS_INFO("Number of erase blocks: %u\n", wear_eb_count);
+ NS_INFO("Average number of erases: %lu\n", avg);
+ NS_INFO("Maximum number of erases: %lu\n", wmax);
+ NS_INFO("Minimum number of erases: %lu\n", wmin);
+ for (i = 0; i < 10; ++i) {
+ unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
+ if (from > decile_max[i])
+ continue;
+ NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n",
+ from,
+ decile_max[i],
+ deciles[i]);
+ }
+ NS_INFO("*** End of Wear Report ***\n");
+}
+
/*
* Returns the string representation of 'state' state.
*/
@@ -822,9 +1213,31 @@ static void read_page(struct nandsim *ns, int num)
NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
memset(ns->buf.byte, 0xFF, num);
} else {
+ unsigned int page_no = ns->regs.row;
NS_DBG("read_page: page %d allocated, reading from %d\n",
ns->regs.row, ns->regs.column + ns->regs.off);
+ if (read_error(page_no)) {
+ int i;
+ memset(ns->buf.byte, 0xFF, num);
+ for (i = 0; i < num; ++i)
+ ns->buf.byte[i] = random32();
+ NS_WARN("simulating read error in page %u\n", page_no);
+ return;
+ }
memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
+ if (bitflips && random32() < (1 << 22)) {
+ int flips = 1;
+ if (bitflips > 1)
+ flips = (random32() % (int) bitflips) + 1;
+ while (flips--) {
+ int pos = random32() % (num * 8);
+ ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
+ NS_WARN("read_page: flipping bit %d in page %d "
+ "reading from %d ecc: corrected=%u failed=%u\n",
+ pos, ns->regs.row, ns->regs.column + ns->regs.off,
+ nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
+ }
+ }
}
}
@@ -883,6 +1296,7 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
{
int num;
int busdiv = ns->busw == 8 ? 1 : 2;
+ unsigned int erase_block_no, page_no;
action &= ACTION_MASK;
@@ -942,14 +1356,24 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
ns->regs.column = 0;
+ erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift);
+
NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
ns->regs.row, NS_RAW_OFFSET(ns));
- NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
+ NS_LOG("erase sector %u\n", erase_block_no);
erase_sector(ns);
NS_MDELAY(erase_delay);
+ if (erase_block_wear)
+ update_wear(erase_block_no);
+
+ if (erase_error(erase_block_no)) {
+ NS_WARN("simulating erase failure in erase block %u\n", erase_block_no);
+ return -1;
+ }
+
break;
case ACTION_PRGPAGE:
@@ -972,6 +1396,8 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
if (prog_page(ns, num) == -1)
return -1;
+ page_no = ns->regs.row;
+
NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
NS_LOG("programm page %d\n", ns->regs.row);
@@ -979,6 +1405,11 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
NS_UDELAY(programm_delay);
NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
+ if (write_error(page_no)) {
+ NS_WARN("simulating write failure in page %u\n", page_no);
+ return -1;
+ }
+
break;
case ACTION_ZEROOFF:
@@ -1503,7 +1934,7 @@ static int __init ns_init_module(void)
{
struct nand_chip *chip;
struct nandsim *nand;
- int retval = -ENOMEM;
+ int retval = -ENOMEM, i;
if (bus_width != 8 && bus_width != 16) {
NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
@@ -1533,6 +1964,8 @@ static int __init ns_init_module(void)
chip->verify_buf = ns_nand_verify_buf;
chip->read_word = ns_nand_read_word;
chip->ecc.mode = NAND_ECC_SOFT;
+ /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
+ /* and 'badblocks' parameters to work */
chip->options |= NAND_SKIP_BBTSCAN;
/*
@@ -1557,6 +1990,15 @@ static int __init ns_init_module(void)
nsmtd->owner = THIS_MODULE;
+ if ((retval = parse_weakblocks()) != 0)
+ goto error;
+
+ if ((retval = parse_weakpages()) != 0)
+ goto error;
+
+ if ((retval = parse_gravepages()) != 0)
+ goto error;
+
if ((retval = nand_scan(nsmtd, 1)) != 0) {
NS_ERR("can't register NAND Simulator\n");
if (retval > 0)
@@ -1564,23 +2006,44 @@ static int __init ns_init_module(void)
goto error;
}
- if ((retval = init_nandsim(nsmtd)) != 0) {
- NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
- goto error;
+ if (overridesize) {
+ u_int32_t new_size = nsmtd->erasesize << overridesize;
+ if (new_size >> overridesize != nsmtd->erasesize) {
+ NS_ERR("overridesize is too big\n");
+ goto err_exit;
+ }
+ /* N.B. This relies on nand_scan not doing anything with the size before we change it */
+ nsmtd->size = new_size;
+ chip->chipsize = new_size;
+ chip->chip_shift = ffs(new_size) - 1;
}
- if ((retval = nand_default_bbt(nsmtd)) != 0) {
- free_nandsim(nand);
- goto error;
- }
+ if ((retval = setup_wear_reporting(nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = init_nandsim(nsmtd)) != 0)
+ goto err_exit;
- /* Register NAND as one big partition */
- add_mtd_partitions(nsmtd, &nand->part, 1);
+ if ((retval = parse_badblocks(nand, nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = nand_default_bbt(nsmtd)) != 0)
+ goto err_exit;
+
+ /* Register NAND partitions */
+ if ((retval = add_mtd_partitions(nsmtd, &nand->partitions[0], nand->nbparts)) != 0)
+ goto err_exit;
return 0;
+err_exit:
+ free_nandsim(nand);
+ nand_release(nsmtd);
+ for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
+ kfree(nand->partitions[i].name);
error:
kfree(nsmtd);
+ free_lists();
return retval;
}
@@ -1593,10 +2056,14 @@ module_init(ns_init_module);
static void __exit ns_cleanup_module(void)
{
struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv);
+ int i;
free_nandsim(ns); /* Free nandsim private resources */
- nand_release(nsmtd); /* Unregisterd drived */
+ nand_release(nsmtd); /* Unregister driver */
+ for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
+ kfree(ns->partitions[i].name);
kfree(nsmtd); /* Free other structures */
+ free_lists();
}
module_exit(ns_cleanup_module);
@@ -1604,4 +2071,3 @@ module_exit(ns_cleanup_module);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Artem B. Bityuckiy");
MODULE_DESCRIPTION ("The NAND flash simulator");
-
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 373bddce8f1c..c257d397d08a 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -2,20 +2,18 @@
# linux/drivers/mtd/onenand/Kconfig
#
-menu "OneNAND Flash Device Drivers"
- depends on MTD != n
-
-config MTD_ONENAND
+menuconfig MTD_ONENAND
tristate "OneNAND Device Support"
depends on MTD
help
This enables support for accessing all type of OneNAND flash
devices. For further information see
- <http://www.samsung.com/Products/Semiconductor/Flash/OneNAND_TM/index.htm>.
+ <http://www.samsung.com/Products/Semiconductor/OneNAND/index.htm>
+
+if MTD_ONENAND
config MTD_ONENAND_VERIFY_WRITE
bool "Verify OneNAND page writes"
- depends on MTD_ONENAND
help
This adds an extra check when data is written to the flash. The
OneNAND flash device internally checks only bits transitioning
@@ -25,13 +23,12 @@ config MTD_ONENAND_VERIFY_WRITE
config MTD_ONENAND_GENERIC
tristate "OneNAND Flash device via platform device driver"
- depends on MTD_ONENAND && ARM
+ depends on ARM
help
Support for OneNAND flash via platform device driver.
config MTD_ONENAND_OTP
bool "OneNAND OTP Support"
- depends on MTD_ONENAND
help
One Block of the NAND Flash Array memory is reserved as
a One-Time Programmable Block memory area.
@@ -43,4 +40,4 @@ config MTD_ONENAND_OTP
OTP block is fully-guaranteed to be a valid block.
-endmenu
+endif # MTD_ONENAND
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 9e14a26ca4e8..000794c6caf5 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -836,9 +836,11 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
int readcol = column;
int readend = column + thislen;
int lastgap = 0;
+ unsigned int i;
uint8_t *oob_buf = this->oob_buf;
- for (free = this->ecclayout->oobfree; free->length; ++free) {
+ free = this->ecclayout->oobfree;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
if (readcol >= lastgap)
readcol += free->offset - lastgap;
if (readend >= lastgap)
@@ -846,7 +848,8 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
lastgap = free->offset + free->length;
}
this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
- for (free = this->ecclayout->oobfree; free->length; ++free) {
+ free = this->ecclayout->oobfree;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
int free_end = free->offset + free->length;
if (free->offset < readend && free_end > readcol) {
int st = max_t(int,free->offset,readcol);
@@ -854,7 +857,7 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
int n = ed - st;
memcpy(buf, oob_buf + st, n);
buf += n;
- } else
+ } else if (column == 0)
break;
}
return 0;
@@ -1280,15 +1283,18 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
int writecol = column;
int writeend = column + thislen;
int lastgap = 0;
+ unsigned int i;
- for (free = this->ecclayout->oobfree; free->length; ++free) {
+ free = this->ecclayout->oobfree;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
if (writecol >= lastgap)
writecol += free->offset - lastgap;
if (writeend >= lastgap)
writeend += free->offset - lastgap;
lastgap = free->offset + free->length;
}
- for (free = this->ecclayout->oobfree; free->length; ++free) {
+ free = this->ecclayout->oobfree;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
int free_end = free->offset + free->length;
if (free->offset < writeend && free_end > writecol) {
int st = max_t(int,free->offset,writecol);
@@ -1296,7 +1302,7 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
int n = ed - st;
memcpy(oob_buf + st, buf, n);
buf += n;
- } else
+ } else if (column == 0)
break;
}
return 0;
@@ -2386,7 +2392,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
* the out of band area
*/
this->ecclayout->oobavail = 0;
- for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
+ this->ecclayout->oobfree[i].length; i++)
this->ecclayout->oobavail +=
this->ecclayout->oobfree[i].length;
mtd->oobavail = this->ecclayout->oobavail;
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
new file mode 100644
index 000000000000..b9daf159a4a7
--- /dev/null
+++ b/drivers/mtd/ubi/Kconfig
@@ -0,0 +1,58 @@
+# drivers/mtd/ubi/Kconfig
+
+menu "UBI - Unsorted block images"
+ depends on MTD
+
+config MTD_UBI
+ tristate "Enable UBI"
+ depends on MTD
+ select CRC32
+ help
+ UBI is a software layer above MTD layer which admits of LVM-like
+ logical volumes on top of MTD devices, hides some complexities of
+ flash chips like wear and bad blocks and provides some other useful
+ capabilities. Please, consult the MTD web site for more details
+ (www.linux-mtd.infradead.org).
+
+config MTD_UBI_WL_THRESHOLD
+ int "UBI wear-leveling threshold"
+ default 4096
+ range 2 65536
+ depends on MTD_UBI
+ help
+ This parameter defines the maximum difference between the highest
+ erase counter value and the lowest erase counter value of eraseblocks
+ of UBI devices. When this threshold is exceeded, UBI starts performing
+ wear leveling by means of moving data from eraseblock with low erase
+ counter to eraseblocks with high erase counter. Leave the default
+ value if unsure.
+
+config MTD_UBI_BEB_RESERVE
+ int "Percentage of reserved eraseblocks for bad eraseblocks handling"
+ default 1
+ range 0 25
+ depends on MTD_UBI
+ help
+ If the MTD device admits of bad eraseblocks (e.g. NAND flash), UBI
+ reserves some amount of physical eraseblocks to handle new bad
+ eraseblocks. For example, if a flash physical eraseblock becomes bad,
+ UBI uses these reserved physical eraseblocks to relocate the bad one.
+ This option specifies how many physical eraseblocks will be reserved
+ for bad eraseblock handling (percents of total number of good flash
+ eraseblocks). If the underlying flash does not admit of bad
+ eraseblocks (e.g. NOR flash), this value is ignored and nothing is
+ reserved. Leave the default value if unsure.
+
+config MTD_UBI_GLUEBI
+ bool "Emulate MTD devices"
+ default n
+ depends on MTD_UBI
+ help
+ This option enables MTD devices emulation on top of UBI volumes: for
+ each UBI volumes an MTD device is created, and all I/O to this MTD
+ device is redirected to the UBI volume. This is handy to make
+ MTD-oriented software (like JFFS2) work on top of UBI. Do not enable
+ this if no legacy software will be used.
+
+source "drivers/mtd/ubi/Kconfig.debug"
+endmenu
diff --git a/drivers/mtd/ubi/Kconfig.debug b/drivers/mtd/ubi/Kconfig.debug
new file mode 100644
index 000000000000..1e2ee22edeff
--- /dev/null
+++ b/drivers/mtd/ubi/Kconfig.debug
@@ -0,0 +1,104 @@
+comment "UBI debugging options"
+ depends on MTD_UBI
+
+config MTD_UBI_DEBUG
+ bool "UBI debugging"
+ depends on SYSFS
+ depends on MTD_UBI
+ select DEBUG_FS
+ select KALLSYMS_ALL
+ help
+ This option enables UBI debugging.
+
+config MTD_UBI_DEBUG_MSG
+ bool "UBI debugging messages"
+ depends on MTD_UBI_DEBUG
+ default n
+ help
+ This option enables UBI debugging messages.
+
+config MTD_UBI_DEBUG_PARANOID
+ bool "Extra self-checks"
+ default n
+ depends on MTD_UBI_DEBUG
+ help
+ This option enables extra checks in UBI code. Note this slows UBI down
+ significantly.
+
+config MTD_UBI_DEBUG_DISABLE_BGT
+ bool "Do not enable the UBI background thread"
+ depends on MTD_UBI_DEBUG
+ default n
+ help
+ This option switches the background thread off by default. The thread
+ may be also be enabled/disabled via UBI sysfs.
+
+config MTD_UBI_DEBUG_USERSPACE_IO
+ bool "Direct user-space write/erase support"
+ default n
+ depends on MTD_UBI_DEBUG
+ help
+ By default, users cannot directly write and erase individual
+ eraseblocks of dynamic volumes, and have to use update operation
+ instead. This option enables this capability - it is very useful for
+ debugging and testing.
+
+config MTD_UBI_DEBUG_EMULATE_BITFLIPS
+ bool "Emulate flash bit-flips"
+ depends on MTD_UBI_DEBUG
+ default n
+ help
+ This option emulates bit-flips with probability 1/50, which in turn
+ causes scrubbing. Useful for debugging and stressing UBI.
+
+config MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
+ bool "Emulate flash write failures"
+ depends on MTD_UBI_DEBUG
+ default n
+ help
+ This option emulates write failures with probability 1/100. Useful for
+ debugging and testing how UBI handlines errors.
+
+config MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
+ bool "Emulate flash erase failures"
+ depends on MTD_UBI_DEBUG
+ default n
+ help
+ This option emulates erase failures with probability 1/100. Useful for
+ debugging and testing how UBI handlines errors.
+
+menu "Additional UBI debugging messages"
+ depends on MTD_UBI_DEBUG
+
+config MTD_UBI_DEBUG_MSG_BLD
+ bool "Additional UBI initialization and build messages"
+ default n
+ depends on MTD_UBI_DEBUG
+ help
+ This option enables detailed UBI initialization and device build
+ debugging messages.
+
+config MTD_UBI_DEBUG_MSG_EBA
+ bool "Eraseblock association unit messages"
+ default n
+ depends on MTD_UBI_DEBUG
+ help
+ This option enables debugging messages from the UBI eraseblock
+ association unit.
+
+config MTD_UBI_DEBUG_MSG_WL
+ bool "Wear-leveling unit messages"
+ default n
+ depends on MTD_UBI_DEBUG
+ help
+ This option enables debugging messages from the UBI wear-leveling
+ unit.
+
+config MTD_UBI_DEBUG_MSG_IO
+ bool "Input/output unit messages"
+ default n
+ depends on MTD_UBI_DEBUG
+ help
+ This option enables debugging messages from the UBI input/output unit.
+
+endmenu # UBI debugging messages
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
new file mode 100644
index 000000000000..dd834e04151b
--- /dev/null
+++ b/drivers/mtd/ubi/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_MTD_UBI) += ubi.o
+
+ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o scan.o
+ubi-y += misc.o
+
+ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
+ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
new file mode 100644
index 000000000000..555d594d1811
--- /dev/null
+++ b/drivers/mtd/ubi/build.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём),
+ * Frank Haverkamp
+ */
+
+/*
+ * This file includes UBI initialization and building of UBI devices. At the
+ * moment UBI devices may only be added while UBI is initialized, but dynamic
+ * device add/remove functionality is planned. Also, at the moment we only
+ * attach UBI devices by scanning, which will become a bottleneck when flashes
+ * reach certain large size. Then one may improve UBI and add other methods.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/stringify.h>
+#include <linux/stat.h>
+#include "ubi.h"
+
+/* Maximum length of the 'mtd=' parameter */
+#define MTD_PARAM_LEN_MAX 64
+
+/**
+ * struct mtd_dev_param - MTD device parameter description data structure.
+ * @name: MTD device name or number string
+ * @vid_hdr_offs: VID header offset
+ * @data_offs: data offset
+ */
+struct mtd_dev_param
+{
+ char name[MTD_PARAM_LEN_MAX];
+ int vid_hdr_offs;
+ int data_offs;
+};
+
+/* Numbers of elements set in the @mtd_dev_param array */
+static int mtd_devs = 0;
+
+/* MTD devices specification parameters */
+static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];
+
+/* Number of UBI devices in system */
+int ubi_devices_cnt;
+
+/* All UBI devices in system */
+struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
+
+/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
+struct class *ubi_class;
+
+/* "Show" method for files in '/<sysfs>/class/ubi/' */
+static ssize_t ubi_version_show(struct class *class, char *buf)
+{
+ return sprintf(buf, "%d\n", UBI_VERSION);
+}
+
+/* UBI version attribute ('/<sysfs>/class/ubi/version') */
+static struct class_attribute ubi_version =
+ __ATTR(version, S_IRUGO, ubi_version_show, NULL);
+
+static ssize_t dev_attribute_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+/* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */
+static struct device_attribute dev_eraseblock_size =
+ __ATTR(eraseblock_size, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_avail_eraseblocks =
+ __ATTR(avail_eraseblocks, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_total_eraseblocks =
+ __ATTR(total_eraseblocks, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_volumes_count =
+ __ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_max_ec =
+ __ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_reserved_for_bad =
+ __ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_bad_peb_count =
+ __ATTR(bad_peb_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_max_vol_count =
+ __ATTR(max_vol_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_min_io_size =
+ __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_bgt_enabled =
+ __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL);
+
+/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */
+static ssize_t dev_attribute_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct ubi_device *ubi;
+
+ ubi = container_of(dev, struct ubi_device, dev);
+ if (attr == &dev_eraseblock_size)
+ return sprintf(buf, "%d\n", ubi->leb_size);
+ else if (attr == &dev_avail_eraseblocks)
+ return sprintf(buf, "%d\n", ubi->avail_pebs);
+ else if (attr == &dev_total_eraseblocks)
+ return sprintf(buf, "%d\n", ubi->good_peb_count);
+ else if (attr == &dev_volumes_count)
+ return sprintf(buf, "%d\n", ubi->vol_count);
+ else if (attr == &dev_max_ec)
+ return sprintf(buf, "%d\n", ubi->max_ec);
+ else if (attr == &dev_reserved_for_bad)
+ return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
+ else if (attr == &dev_bad_peb_count)
+ return sprintf(buf, "%d\n", ubi->bad_peb_count);
+ else if (attr == &dev_max_vol_count)
+ return sprintf(buf, "%d\n", ubi->vtbl_slots);
+ else if (attr == &dev_min_io_size)
+ return sprintf(buf, "%d\n", ubi->min_io_size);
+ else if (attr == &dev_bgt_enabled)
+ return sprintf(buf, "%d\n", ubi->thread_enabled);
+ else
+ BUG();
+
+ return 0;
+}
+
+/* Fake "release" method for UBI devices */
+static void dev_release(struct device *dev) { }
+
+/**
+ * ubi_sysfs_init - initialize sysfs for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int ubi_sysfs_init(struct ubi_device *ubi)
+{
+ int err;
+
+ ubi->dev.release = dev_release;
+ ubi->dev.devt = MKDEV(ubi->major, 0);
+ ubi->dev.class = ubi_class;
+ sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num);
+ err = device_register(&ubi->dev);
+ if (err)
+ goto out;
+
+ err = device_create_file(&ubi->dev, &dev_eraseblock_size);
+ if (err)
+ goto out_unregister;
+ err = device_create_file(&ubi->dev, &dev_avail_eraseblocks);
+ if (err)
+ goto out_eraseblock_size;
+ err = device_create_file(&ubi->dev, &dev_total_eraseblocks);
+ if (err)
+ goto out_avail_eraseblocks;
+ err = device_create_file(&ubi->dev, &dev_volumes_count);
+ if (err)
+ goto out_total_eraseblocks;
+ err = device_create_file(&ubi->dev, &dev_max_ec);
+ if (err)
+ goto out_volumes_count;
+ err = device_create_file(&ubi->dev, &dev_reserved_for_bad);
+ if (err)
+ goto out_volumes_max_ec;
+ err = device_create_file(&ubi->dev, &dev_bad_peb_count);
+ if (err)
+ goto out_reserved_for_bad;
+ err = device_create_file(&ubi->dev, &dev_max_vol_count);
+ if (err)
+ goto out_bad_peb_count;
+ err = device_create_file(&ubi->dev, &dev_min_io_size);
+ if (err)
+ goto out_max_vol_count;
+ err = device_create_file(&ubi->dev, &dev_bgt_enabled);
+ if (err)
+ goto out_min_io_size;
+
+ return 0;
+
+out_min_io_size:
+ device_remove_file(&ubi->dev, &dev_min_io_size);
+out_max_vol_count:
+ device_remove_file(&ubi->dev, &dev_max_vol_count);
+out_bad_peb_count:
+ device_remove_file(&ubi->dev, &dev_bad_peb_count);
+out_reserved_for_bad:
+ device_remove_file(&ubi->dev, &dev_reserved_for_bad);
+out_volumes_max_ec:
+ device_remove_file(&ubi->dev, &dev_max_ec);
+out_volumes_count:
+ device_remove_file(&ubi->dev, &dev_volumes_count);
+out_total_eraseblocks:
+ device_remove_file(&ubi->dev, &dev_total_eraseblocks);
+out_avail_eraseblocks:
+ device_remove_file(&ubi->dev, &dev_avail_eraseblocks);
+out_eraseblock_size:
+ device_remove_file(&ubi->dev, &dev_eraseblock_size);
+out_unregister:
+ device_unregister(&ubi->dev);
+out:
+ ubi_err("failed to initialize sysfs for %s", ubi->ubi_name);
+ return err;
+}
+
+/**
+ * ubi_sysfs_close - close sysfs for an UBI device.
+ * @ubi: UBI device description object
+ */
+static void ubi_sysfs_close(struct ubi_device *ubi)
+{
+ device_remove_file(&ubi->dev, &dev_bgt_enabled);
+ device_remove_file(&ubi->dev, &dev_min_io_size);
+ device_remove_file(&ubi->dev, &dev_max_vol_count);
+ device_remove_file(&ubi->dev, &dev_bad_peb_count);
+ device_remove_file(&ubi->dev, &dev_reserved_for_bad);
+ device_remove_file(&ubi->dev, &dev_max_ec);
+ device_remove_file(&ubi->dev, &dev_volumes_count);
+ device_remove_file(&ubi->dev, &dev_total_eraseblocks);
+ device_remove_file(&ubi->dev, &dev_avail_eraseblocks);
+ device_remove_file(&ubi->dev, &dev_eraseblock_size);
+ device_unregister(&ubi->dev);
+}
+
+/**
+ * kill_volumes - destroy all volumes.
+ * @ubi: UBI device description object
+ */
+static void kill_volumes(struct ubi_device *ubi)
+{
+ int i;
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i])
+ ubi_free_volume(ubi, i);
+}
+
+/**
+ * uif_init - initialize user interfaces for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int uif_init(struct ubi_device *ubi)
+{
+ int i, err;
+ dev_t dev;
+
+ mutex_init(&ubi->vtbl_mutex);
+ spin_lock_init(&ubi->volumes_lock);
+
+ sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
+
+ /*
+ * Major numbers for the UBI character devices are allocated
+ * dynamically. Major numbers of volume character devices are
+ * equivalent to ones of the corresponding UBI character device. Minor
+ * numbers of UBI character devices are 0, while minor numbers of
+ * volume character devices start from 1. Thus, we allocate one major
+ * number and ubi->vtbl_slots + 1 minor numbers.
+ */
+ err = alloc_chrdev_region(&dev, 0, ubi->vtbl_slots + 1, ubi->ubi_name);
+ if (err) {
+ ubi_err("cannot register UBI character devices");
+ return err;
+ }
+
+ cdev_init(&ubi->cdev, &ubi_cdev_operations);
+ ubi->major = MAJOR(dev);
+ dbg_msg("%s major is %u", ubi->ubi_name, ubi->major);
+ ubi->cdev.owner = THIS_MODULE;
+
+ dev = MKDEV(ubi->major, 0);
+ err = cdev_add(&ubi->cdev, dev, 1);
+ if (err) {
+ ubi_err("cannot add character device %s", ubi->ubi_name);
+ goto out_unreg;
+ }
+
+ err = ubi_sysfs_init(ubi);
+ if (err)
+ goto out_cdev;
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i]) {
+ err = ubi_add_volume(ubi, i);
+ if (err)
+ goto out_volumes;
+ }
+
+ return 0;
+
+out_volumes:
+ kill_volumes(ubi);
+ ubi_sysfs_close(ubi);
+out_cdev:
+ cdev_del(&ubi->cdev);
+out_unreg:
+ unregister_chrdev_region(MKDEV(ubi->major, 0),
+ ubi->vtbl_slots + 1);
+ return err;
+}
+
+/**
+ * uif_close - close user interfaces for an UBI device.
+ * @ubi: UBI device description object
+ */
+static void uif_close(struct ubi_device *ubi)
+{
+ kill_volumes(ubi);
+ ubi_sysfs_close(ubi);
+ cdev_del(&ubi->cdev);
+ unregister_chrdev_region(MKDEV(ubi->major, 0), ubi->vtbl_slots + 1);
+}
+
+/**
+ * attach_by_scanning - attach an MTD device using scanning method.
+ * @ubi: UBI device descriptor
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ *
+ * Note, currently this is the only method to attach UBI devices. Hopefully in
+ * the future we'll have more scalable attaching methods and avoid full media
+ * scanning. But even in this case scanning will be needed as a fall-back
+ * attaching method if there are some on-flash table corruptions.
+ */
+static int attach_by_scanning(struct ubi_device *ubi)
+{
+ int err;
+ struct ubi_scan_info *si;
+
+ si = ubi_scan(ubi);
+ if (IS_ERR(si))
+ return PTR_ERR(si);
+
+ ubi->bad_peb_count = si->bad_peb_count;
+ ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count;
+ ubi->max_ec = si->max_ec;
+ ubi->mean_ec = si->mean_ec;
+
+ err = ubi_read_volume_table(ubi, si);
+ if (err)
+ goto out_si;
+
+ err = ubi_wl_init_scan(ubi, si);
+ if (err)
+ goto out_vtbl;
+
+ err = ubi_eba_init_scan(ubi, si);
+ if (err)
+ goto out_wl;
+
+ ubi_scan_destroy_si(si);
+ return 0;
+
+out_wl:
+ ubi_wl_close(ubi);
+out_vtbl:
+ kfree(ubi->vtbl);
+out_si:
+ ubi_scan_destroy_si(si);
+ return err;
+}
+
+/**
+ * io_init - initialize I/O unit for a given UBI device.
+ * @ubi: UBI device description object
+ *
+ * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are
+ * assumed:
+ * o EC header is always at offset zero - this cannot be changed;
+ * o VID header starts just after the EC header at the closest address
+ * aligned to @io->@hdrs_min_io_size;
+ * o data starts just after the VID header at the closest address aligned to
+ * @io->@min_io_size
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int io_init(struct ubi_device *ubi)
+{
+ if (ubi->mtd->numeraseregions != 0) {
+ /*
+ * Some flashes have several erase regions. Different regions
+ * may have different eraseblock size and other
+ * characteristics. It looks like mostly multi-region flashes
+ * have one "main" region and one or more small regions to
+ * store boot loader code or boot parameters or whatever. I
+ * guess we should just pick the largest region. But this is
+ * not implemented.
+ */
+ ubi_err("multiple regions, not implemented");
+ return -EINVAL;
+ }
+
+ /*
+ * Note, in this implementation we support MTD devices with 0x7FFFFFFF
+ * physical eraseblocks maximum.
+ */
+
+ ubi->peb_size = ubi->mtd->erasesize;
+ ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize;
+ ubi->flash_size = ubi->mtd->size;
+
+ if (ubi->mtd->block_isbad && ubi->mtd->block_markbad)
+ ubi->bad_allowed = 1;
+
+ ubi->min_io_size = ubi->mtd->writesize;
+ ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft;
+
+ /* Make sure minimal I/O unit is power of 2 */
+ if (ubi->min_io_size == 0 ||
+ (ubi->min_io_size & (ubi->min_io_size - 1))) {
+ ubi_err("bad min. I/O unit");
+ return -EINVAL;
+ }
+
+ ubi_assert(ubi->hdrs_min_io_size > 0);
+ ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size);
+ ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0);
+
+ /* Calculate default aligned sizes of EC and VID headers */
+ ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
+ ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
+
+ dbg_msg("min_io_size %d", ubi->min_io_size);
+ dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size);
+ dbg_msg("ec_hdr_alsize %d", ubi->ec_hdr_alsize);
+ dbg_msg("vid_hdr_alsize %d", ubi->vid_hdr_alsize);
+
+ if (ubi->vid_hdr_offset == 0)
+ /* Default offset */
+ ubi->vid_hdr_offset = ubi->vid_hdr_aloffset =
+ ubi->ec_hdr_alsize;
+ else {
+ ubi->vid_hdr_aloffset = ubi->vid_hdr_offset &
+ ~(ubi->hdrs_min_io_size - 1);
+ ubi->vid_hdr_shift = ubi->vid_hdr_offset -
+ ubi->vid_hdr_aloffset;
+ }
+
+ /* Similar for the data offset */
+ if (ubi->leb_start == 0) {
+ ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize;
+ ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
+ }
+
+ dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset);
+ dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset);
+ dbg_msg("vid_hdr_shift %d", ubi->vid_hdr_shift);
+ dbg_msg("leb_start %d", ubi->leb_start);
+
+ /* The shift must be aligned to 32-bit boundary */
+ if (ubi->vid_hdr_shift % 4) {
+ ubi_err("unaligned VID header shift %d",
+ ubi->vid_hdr_shift);
+ return -EINVAL;
+ }
+
+ /* Check sanity */
+ if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
+ ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
+ ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
+ ubi->leb_start % ubi->min_io_size) {
+ ubi_err("bad VID header (%d) or data offsets (%d)",
+ ubi->vid_hdr_offset, ubi->leb_start);
+ return -EINVAL;
+ }
+
+ /*
+ * It may happen that EC and VID headers are situated in one minimal
+ * I/O unit. In this case we can only accept this UBI image in
+ * read-only mode.
+ */
+ if (ubi->vid_hdr_offset + UBI_VID_HDR_SIZE <= ubi->hdrs_min_io_size) {
+ ubi_warn("EC and VID headers are in the same minimal I/O unit, "
+ "switch to read-only mode");
+ ubi->ro_mode = 1;
+ }
+
+ ubi->leb_size = ubi->peb_size - ubi->leb_start;
+
+ if (!(ubi->mtd->flags & MTD_WRITEABLE)) {
+ ubi_msg("MTD device %d is write-protected, attach in "
+ "read-only mode", ubi->mtd->index);
+ ubi->ro_mode = 1;
+ }
+
+ dbg_msg("leb_size %d", ubi->leb_size);
+ dbg_msg("ro_mode %d", ubi->ro_mode);
+
+ /*
+ * Note, ideally, we have to initialize ubi->bad_peb_count here. But
+ * unfortunately, MTD does not provide this information. We should loop
+ * over all physical eraseblocks and invoke mtd->block_is_bad() for
+ * each physical eraseblock. So, we skip ubi->bad_peb_count
+ * uninitialized and initialize it after scanning.
+ */
+
+ return 0;
+}
+
+/**
+ * attach_mtd_dev - attach an MTD device.
+ * @mtd_dev: MTD device name or number string
+ * @vid_hdr_offset: VID header offset
+ * @data_offset: data offset
+ *
+ * This function attaches an MTD device to UBI. It first treats @mtd_dev as the
+ * MTD device name, and tries to open it by this name. If it is unable to open,
+ * it tries to convert @mtd_dev to an integer and open the MTD device by its
+ * number. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset,
+ int data_offset)
+{
+ struct ubi_device *ubi;
+ struct mtd_info *mtd;
+ int i, err;
+
+ mtd = get_mtd_device_nm(mtd_dev);
+ if (IS_ERR(mtd)) {
+ int mtd_num;
+ char *endp;
+
+ if (PTR_ERR(mtd) != -ENODEV)
+ return PTR_ERR(mtd);
+
+ /*
+ * Probably this is not MTD device name but MTD device number -
+ * check this out.
+ */
+ mtd_num = simple_strtoul(mtd_dev, &endp, 0);
+ if (*endp != '\0' || mtd_dev == endp) {
+ ubi_err("incorrect MTD device: \"%s\"", mtd_dev);
+ return -ENODEV;
+ }
+
+ mtd = get_mtd_device(NULL, mtd_num);
+ if (IS_ERR(mtd))
+ return PTR_ERR(mtd);
+ }
+
+ /* Check if we already have the same MTD device attached */
+ for (i = 0; i < ubi_devices_cnt; i++)
+ if (ubi_devices[i]->mtd->index == mtd->index) {
+ ubi_err("mtd%d is already attached to ubi%d",
+ mtd->index, i);
+ err = -EINVAL;
+ goto out_mtd;
+ }
+
+ ubi = ubi_devices[ubi_devices_cnt] = kzalloc(sizeof(struct ubi_device),
+ GFP_KERNEL);
+ if (!ubi) {
+ err = -ENOMEM;
+ goto out_mtd;
+ }
+
+ ubi->ubi_num = ubi_devices_cnt;
+ ubi->mtd = mtd;
+
+ dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d",
+ ubi->mtd->index, ubi_devices_cnt, vid_hdr_offset, data_offset);
+
+ ubi->vid_hdr_offset = vid_hdr_offset;
+ ubi->leb_start = data_offset;
+ err = io_init(ubi);
+ if (err)
+ goto out_free;
+
+ err = attach_by_scanning(ubi);
+ if (err) {
+ dbg_err("failed to attach by scanning, error %d", err);
+ goto out_free;
+ }
+
+ err = uif_init(ubi);
+ if (err)
+ goto out_detach;
+
+ ubi_devices_cnt += 1;
+
+ ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi_devices_cnt);
+ ubi_msg("MTD device name: \"%s\"", ubi->mtd->name);
+ ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20);
+ ubi_msg("physical eraseblock size: %d bytes (%d KiB)",
+ ubi->peb_size, ubi->peb_size >> 10);
+ ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size);
+ ubi_msg("number of good PEBs: %d", ubi->good_peb_count);
+ ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count);
+ ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size);
+ ubi_msg("VID header offset: %d (aligned %d)",
+ ubi->vid_hdr_offset, ubi->vid_hdr_aloffset);
+ ubi_msg("data offset: %d", ubi->leb_start);
+ ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots);
+ ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD);
+ ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT);
+ ubi_msg("number of user volumes: %d",
+ ubi->vol_count - UBI_INT_VOL_COUNT);
+ ubi_msg("available PEBs: %d", ubi->avail_pebs);
+ ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs);
+ ubi_msg("number of PEBs reserved for bad PEB handling: %d",
+ ubi->beb_rsvd_pebs);
+ ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);
+
+ /* Enable the background thread */
+ if (!DBG_DISABLE_BGT) {
+ ubi->thread_enabled = 1;
+ wake_up_process(ubi->bgt_thread);
+ }
+
+ return 0;
+
+out_detach:
+ ubi_eba_close(ubi);
+ ubi_wl_close(ubi);
+ kfree(ubi->vtbl);
+out_free:
+ kfree(ubi);
+out_mtd:
+ put_mtd_device(mtd);
+ ubi_devices[ubi_devices_cnt] = NULL;
+ return err;
+}
+
+/**
+ * detach_mtd_dev - detach an MTD device.
+ * @ubi: UBI device description object
+ */
+static void detach_mtd_dev(struct ubi_device *ubi)
+{
+ int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index;
+
+ dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
+ uif_close(ubi);
+ ubi_eba_close(ubi);
+ ubi_wl_close(ubi);
+ kfree(ubi->vtbl);
+ put_mtd_device(ubi->mtd);
+ kfree(ubi_devices[ubi_num]);
+ ubi_devices[ubi_num] = NULL;
+ ubi_devices_cnt -= 1;
+ ubi_assert(ubi_devices_cnt >= 0);
+ ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num);
+}
+
+static int __init ubi_init(void)
+{
+ int err, i, k;
+
+ /* Ensure that EC and VID headers have correct size */
+ BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
+ BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
+
+ if (mtd_devs > UBI_MAX_DEVICES) {
+ printk("UBI error: too many MTD devices, maximum is %d\n",
+ UBI_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+ ubi_class = class_create(THIS_MODULE, UBI_NAME_STR);
+ if (IS_ERR(ubi_class))
+ return PTR_ERR(ubi_class);
+
+ err = class_create_file(ubi_class, &ubi_version);
+ if (err)
+ goto out_class;
+
+ /* Attach MTD devices */
+ for (i = 0; i < mtd_devs; i++) {
+ struct mtd_dev_param *p = &mtd_dev_param[i];
+
+ cond_resched();
+
+ if (!p->name) {
+ dbg_err("empty name");
+ err = -EINVAL;
+ goto out_detach;
+ }
+
+ err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs);
+ if (err)
+ goto out_detach;
+ }
+
+ return 0;
+
+out_detach:
+ for (k = 0; k < i; k++)
+ detach_mtd_dev(ubi_devices[k]);
+ class_remove_file(ubi_class, &ubi_version);
+out_class:
+ class_destroy(ubi_class);
+ return err;
+}
+module_init(ubi_init);
+
+static void __exit ubi_exit(void)
+{
+ int i, n = ubi_devices_cnt;
+
+ for (i = 0; i < n; i++)
+ detach_mtd_dev(ubi_devices[i]);
+ class_remove_file(ubi_class, &ubi_version);
+ class_destroy(ubi_class);
+}
+module_exit(ubi_exit);
+
+/**
+ * bytes_str_to_int - convert a string representing number of bytes to an
+ * integer.
+ * @str: the string to convert
+ *
+ * This function returns positive resulting integer in case of success and a
+ * negative error code in case of failure.
+ */
+static int __init bytes_str_to_int(const char *str)
+{
+ char *endp;
+ unsigned long result;
+
+ result = simple_strtoul(str, &endp, 0);
+ if (str == endp || result < 0) {
+ printk("UBI error: incorrect bytes count: \"%s\"\n", str);
+ return -EINVAL;
+ }
+
+ switch (*endp) {
+ case 'G':
+ result *= 1024;
+ case 'M':
+ result *= 1024;
+ case 'K':
+ case 'k':
+ result *= 1024;
+ if (endp[1] == 'i' && (endp[2] == '\0' ||
+ endp[2] == 'B' || endp[2] == 'b'))
+ endp += 2;
+ case '\0':
+ break;
+ default:
+ printk("UBI error: incorrect bytes count: \"%s\"\n", str);
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+/**
+ * ubi_mtd_param_parse - parse the 'mtd=' UBI parameter.
+ * @val: the parameter value to parse
+ * @kp: not used
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of error.
+ */
+static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
+{
+ int i, len;
+ struct mtd_dev_param *p;
+ char buf[MTD_PARAM_LEN_MAX];
+ char *pbuf = &buf[0];
+ char *tokens[3] = {NULL, NULL, NULL};
+
+ if (mtd_devs == UBI_MAX_DEVICES) {
+ printk("UBI error: too many parameters, max. is %d\n",
+ UBI_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+ len = strnlen(val, MTD_PARAM_LEN_MAX);
+ if (len == MTD_PARAM_LEN_MAX) {
+ printk("UBI error: parameter \"%s\" is too long, max. is %d\n",
+ val, MTD_PARAM_LEN_MAX);
+ return -EINVAL;
+ }
+
+ if (len == 0) {
+ printk("UBI warning: empty 'mtd=' parameter - ignored\n");
+ return 0;
+ }
+
+ strcpy(buf, val);
+
+ /* Get rid of the final newline */
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = 0;
+
+ for (i = 0; i < 3; i++)
+ tokens[i] = strsep(&pbuf, ",");
+
+ if (pbuf) {
+ printk("UBI error: too many arguments at \"%s\"\n", val);
+ return -EINVAL;
+ }
+
+ if (tokens[0] == '\0')
+ return -EINVAL;
+
+ p = &mtd_dev_param[mtd_devs];
+ strcpy(&p->name[0], tokens[0]);
+
+ if (tokens[1])
+ p->vid_hdr_offs = bytes_str_to_int(tokens[1]);
+ if (tokens[2])
+ p->data_offs = bytes_str_to_int(tokens[2]);
+
+ if (p->vid_hdr_offs < 0)
+ return p->vid_hdr_offs;
+ if (p->data_offs < 0)
+ return p->data_offs;
+
+ mtd_devs += 1;
+ return 0;
+}
+
+module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000);
+MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: "
+ "mtd=<name|num>[,<vid_hdr_offs>,<data_offs>]. "
+ "Multiple \"mtd\" parameters may be specified.\n"
+ "MTD devices may be specified by their number or name. "
+ "Optional \"vid_hdr_offs\" and \"data_offs\" parameters "
+ "specify UBI VID header position and data starting "
+ "position to be used by UBI.\n"
+ "Example: mtd=content,1984,2048 mtd=4 - attach MTD device"
+ "with name content using VID header offset 1984 and data "
+ "start 2048, and MTD device number 4 using default "
+ "offsets");
+
+MODULE_VERSION(__stringify(UBI_VERSION));
+MODULE_DESCRIPTION("UBI - Unsorted Block Images");
+MODULE_AUTHOR("Artem Bityutskiy");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
new file mode 100644
index 000000000000..6612eb79bf17
--- /dev/null
+++ b/drivers/mtd/ubi/cdev.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file includes implementation of UBI character device operations.
+ *
+ * There are two kinds of character devices in UBI: UBI character devices and
+ * UBI volume character devices. UBI character devices allow users to
+ * manipulate whole volumes: create, remove, and re-size them. Volume character
+ * devices provide volume I/O capabilities.
+ *
+ * Major and minor numbers are assigned dynamically to both UBI and volume
+ * character devices.
+ */
+
+#include <linux/module.h>
+#include <linux/stat.h>
+#include <linux/ioctl.h>
+#include <linux/capability.h>
+#include <mtd/ubi-user.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+/*
+ * Maximum sequence numbers of UBI and volume character device IOCTLs (direct
+ * logical eraseblock erase is a debug-only feature).
+ */
+#define UBI_CDEV_IOC_MAX_SEQ 2
+#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
+#define VOL_CDEV_IOC_MAX_SEQ 1
+#else
+#define VOL_CDEV_IOC_MAX_SEQ 2
+#endif
+
+/**
+ * major_to_device - get UBI device object by character device major number.
+ * @major: major number
+ *
+ * This function returns a pointer to the UBI device object.
+ */
+static struct ubi_device *major_to_device(int major)
+{
+ int i;
+
+ for (i = 0; i < ubi_devices_cnt; i++)
+ if (ubi_devices[i] && ubi_devices[i]->major == major)
+ return ubi_devices[i];
+ BUG();
+}
+
+/**
+ * get_exclusive - get exclusive access to an UBI volume.
+ * @desc: volume descriptor
+ *
+ * This function changes UBI volume open mode to "exclusive". Returns previous
+ * mode value (positive integer) in case of success and a negative error code
+ * in case of failure.
+ */
+static int get_exclusive(struct ubi_volume_desc *desc)
+{
+ int users, err;
+ struct ubi_volume *vol = desc->vol;
+
+ spin_lock(&vol->ubi->volumes_lock);
+ users = vol->readers + vol->writers + vol->exclusive;
+ ubi_assert(users > 0);
+ if (users > 1) {
+ dbg_err("%d users for volume %d", users, vol->vol_id);
+ err = -EBUSY;
+ } else {
+ vol->readers = vol->writers = 0;
+ vol->exclusive = 1;
+ err = desc->mode;
+ desc->mode = UBI_EXCLUSIVE;
+ }
+ spin_unlock(&vol->ubi->volumes_lock);
+
+ return err;
+}
+
+/**
+ * revoke_exclusive - revoke exclusive mode.
+ * @desc: volume descriptor
+ * @mode: new mode to switch to
+ */
+static void revoke_exclusive(struct ubi_volume_desc *desc, int mode)
+{
+ struct ubi_volume *vol = desc->vol;
+
+ spin_lock(&vol->ubi->volumes_lock);
+ ubi_assert(vol->readers == 0 && vol->writers == 0);
+ ubi_assert(vol->exclusive == 1 && desc->mode == UBI_EXCLUSIVE);
+ vol->exclusive = 0;
+ if (mode == UBI_READONLY)
+ vol->readers = 1;
+ else if (mode == UBI_READWRITE)
+ vol->writers = 1;
+ else
+ vol->exclusive = 1;
+ spin_unlock(&vol->ubi->volumes_lock);
+
+ desc->mode = mode;
+}
+
+static int vol_cdev_open(struct inode *inode, struct file *file)
+{
+ struct ubi_volume_desc *desc;
+ const struct ubi_device *ubi = major_to_device(imajor(inode));
+ int vol_id = iminor(inode) - 1;
+ int mode;
+
+ if (file->f_mode & FMODE_WRITE)
+ mode = UBI_READWRITE;
+ else
+ mode = UBI_READONLY;
+
+ dbg_msg("open volume %d, mode %d", vol_id, mode);
+
+ desc = ubi_open_volume(ubi->ubi_num, vol_id, mode);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ file->private_data = desc;
+ return 0;
+}
+
+static int vol_cdev_release(struct inode *inode, struct file *file)
+{
+ struct ubi_volume_desc *desc = file->private_data;
+ struct ubi_volume *vol = desc->vol;
+
+ dbg_msg("release volume %d, mode %d", vol->vol_id, desc->mode);
+
+ if (vol->updating) {
+ ubi_warn("update of volume %d not finished, volume is damaged",
+ vol->vol_id);
+ vol->updating = 0;
+ kfree(vol->upd_buf);
+ }
+
+ ubi_close_volume(desc);
+ return 0;
+}
+
+static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
+{
+ struct ubi_volume_desc *desc = file->private_data;
+ struct ubi_volume *vol = desc->vol;
+ loff_t new_offset;
+
+ if (vol->updating) {
+ /* Update is in progress, seeking is prohibited */
+ dbg_err("updating");
+ return -EBUSY;
+ }
+
+ switch (origin) {
+ case 0: /* SEEK_SET */
+ new_offset = offset;
+ break;
+ case 1: /* SEEK_CUR */
+ new_offset = file->f_pos + offset;
+ break;
+ case 2: /* SEEK_END */
+ new_offset = vol->used_bytes + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (new_offset < 0 || new_offset > vol->used_bytes) {
+ dbg_err("bad seek %lld", new_offset);
+ return -EINVAL;
+ }
+
+ dbg_msg("seek volume %d, offset %lld, origin %d, new offset %lld",
+ vol->vol_id, offset, origin, new_offset);
+
+ file->f_pos = new_offset;
+ return new_offset;
+}
+
+static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
+ loff_t *offp)
+{
+ struct ubi_volume_desc *desc = file->private_data;
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size;
+ size_t count_save = count;
+ void *tbuf;
+ uint64_t tmp;
+
+ dbg_msg("read %zd bytes from offset %lld of volume %d",
+ count, *offp, vol_id);
+
+ if (vol->updating) {
+ dbg_err("updating");
+ return -EBUSY;
+ }
+ if (vol->upd_marker) {
+ dbg_err("damaged volume, update marker is set");
+ return -EBADF;
+ }
+ if (*offp == vol->used_bytes || count == 0)
+ return 0;
+
+ if (vol->corrupted)
+ dbg_msg("read from corrupted volume %d", vol_id);
+
+ if (*offp + count > vol->used_bytes)
+ count_save = count = vol->used_bytes - *offp;
+
+ tbuf_size = vol->usable_leb_size;
+ if (count < tbuf_size)
+ tbuf_size = ALIGN(count, ubi->min_io_size);
+ tbuf = kmalloc(tbuf_size, GFP_KERNEL);
+ if (!tbuf)
+ return -ENOMEM;
+
+ len = count > tbuf_size ? tbuf_size : count;
+
+ tmp = *offp;
+ off = do_div(tmp, vol->usable_leb_size);
+ lnum = tmp;
+
+ do {
+ cond_resched();
+
+ if (off + len >= vol->usable_leb_size)
+ len = vol->usable_leb_size - off;
+
+ err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0);
+ if (err)
+ break;
+
+ off += len;
+ if (off == vol->usable_leb_size) {
+ lnum += 1;
+ off -= vol->usable_leb_size;
+ }
+
+ count -= len;
+ *offp += len;
+
+ err = copy_to_user(buf, tbuf, len);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ buf += len;
+ len = count > tbuf_size ? tbuf_size : count;
+ } while (count);
+
+ kfree(tbuf);
+ return err ? err : count_save - count;
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
+
+/*
+ * This function allows to directly write to dynamic UBI volumes, without
+ * issuing the volume update operation. Available only as a debugging feature.
+ * Very useful for testing UBI.
+ */
+static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *offp)
+{
+ struct ubi_volume_desc *desc = file->private_data;
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0;
+ size_t count_save = count;
+ char *tbuf;
+ uint64_t tmp;
+
+ dbg_msg("requested: write %zd bytes to offset %lld of volume %u",
+ count, *offp, desc->vol->vol_id);
+
+ if (vol->vol_type == UBI_STATIC_VOLUME)
+ return -EROFS;
+
+ tmp = *offp;
+ off = do_div(tmp, vol->usable_leb_size);
+ lnum = tmp;
+
+ if (off % ubi->min_io_size) {
+ dbg_err("unaligned position");
+ return -EINVAL;
+ }
+
+ if (*offp + count > vol->used_bytes)
+ count_save = count = vol->used_bytes - *offp;
+
+ /* We can write only in fractions of the minimum I/O unit */
+ if (count % ubi->min_io_size) {
+ dbg_err("unaligned write length");
+ return -EINVAL;
+ }
+
+ tbuf_size = vol->usable_leb_size;
+ if (count < tbuf_size)
+ tbuf_size = ALIGN(count, ubi->min_io_size);
+ tbuf = kmalloc(tbuf_size, GFP_KERNEL);
+ if (!tbuf)
+ return -ENOMEM;
+
+ len = count > tbuf_size ? tbuf_size : count;
+
+ while (count) {
+ cond_resched();
+
+ if (off + len >= vol->usable_leb_size)
+ len = vol->usable_leb_size - off;
+
+ err = copy_from_user(tbuf, buf, len);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len,
+ UBI_UNKNOWN);
+ if (err)
+ break;
+
+ off += len;
+ if (off == vol->usable_leb_size) {
+ lnum += 1;
+ off -= vol->usable_leb_size;
+ }
+
+ count -= len;
+ *offp += len;
+ buf += len;
+ len = count > tbuf_size ? tbuf_size : count;
+ }
+
+ kfree(tbuf);
+ return err ? err : count_save - count;
+}
+
+#else
+#define vol_cdev_direct_write(file, buf, count, offp) -EPERM
+#endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */
+
+static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *offp)
+{
+ int err = 0;
+ struct ubi_volume_desc *desc = file->private_data;
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+
+ if (!vol->updating)
+ return vol_cdev_direct_write(file, buf, count, offp);
+
+ err = ubi_more_update_data(ubi, vol->vol_id, buf, count);
+ if (err < 0) {
+ ubi_err("cannot write %zd bytes of update data", count);
+ return err;
+ }
+
+ if (err) {
+ /*
+ * Update is finished, @err contains number of actually written
+ * bytes now.
+ */
+ count = err;
+
+ err = ubi_check_volume(ubi, vol->vol_id);
+ if (err < 0)
+ return err;
+
+ if (err) {
+ ubi_warn("volume %d on UBI device %d is corrupted",
+ vol->vol_id, ubi->ubi_num);
+ vol->corrupted = 1;
+ }
+ vol->checked = 1;
+ revoke_exclusive(desc, UBI_READWRITE);
+ }
+
+ *offp += count;
+ return count;
+}
+
+static int vol_cdev_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct ubi_volume_desc *desc = file->private_data;
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ void __user *argp = (void __user *)arg;
+
+ if (_IOC_NR(cmd) > VOL_CDEV_IOC_MAX_SEQ ||
+ _IOC_TYPE(cmd) != UBI_VOL_IOC_MAGIC)
+ return -ENOTTY;
+
+ if (_IOC_DIR(cmd) && _IOC_READ)
+ err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd));
+ else if (_IOC_DIR(cmd) && _IOC_WRITE)
+ err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd));
+ if (err)
+ return -EFAULT;
+
+ switch (cmd) {
+
+ /* Volume update command */
+ case UBI_IOCVOLUP:
+ {
+ int64_t bytes, rsvd_bytes;
+
+ if (!capable(CAP_SYS_RESOURCE)) {
+ err = -EPERM;
+ break;
+ }
+
+ err = copy_from_user(&bytes, argp, sizeof(int64_t));
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (desc->mode == UBI_READONLY) {
+ err = -EROFS;
+ break;
+ }
+
+ rsvd_bytes = vol->reserved_pebs * (ubi->leb_size-vol->data_pad);
+ if (bytes < 0 || bytes > rsvd_bytes) {
+ err = -EINVAL;
+ break;
+ }
+
+ err = get_exclusive(desc);
+ if (err < 0)
+ break;
+
+ err = ubi_start_update(ubi, vol->vol_id, bytes);
+ if (bytes == 0)
+ revoke_exclusive(desc, UBI_READWRITE);
+
+ file->f_pos = 0;
+ break;
+ }
+
+#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
+ /* Logical eraseblock erasure command */
+ case UBI_IOCEBER:
+ {
+ int32_t lnum;
+
+ err = __get_user(lnum, (__user int32_t *)argp);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (desc->mode == UBI_READONLY) {
+ err = -EROFS;
+ break;
+ }
+
+ if (lnum < 0 || lnum >= vol->reserved_pebs) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (vol->vol_type != UBI_DYNAMIC_VOLUME) {
+ err = -EROFS;
+ break;
+ }
+
+ dbg_msg("erase LEB %d:%d", vol->vol_id, lnum);
+ err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum);
+ if (err)
+ break;
+
+ err = ubi_wl_flush(ubi);
+ break;
+ }
+#endif
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * verify_mkvol_req - verify volume creation request.
+ * @ubi: UBI device description object
+ * @req: the request to check
+ *
+ * This function zero if the request is correct, and %-EINVAL if not.
+ */
+static int verify_mkvol_req(const struct ubi_device *ubi,
+ const struct ubi_mkvol_req *req)
+{
+ int n, err = -EINVAL;
+
+ if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 ||
+ req->name_len < 0)
+ goto bad;
+
+ if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) &&
+ req->vol_id != UBI_VOL_NUM_AUTO)
+ goto bad;
+
+ if (req->alignment == 0)
+ goto bad;
+
+ if (req->bytes == 0)
+ goto bad;
+
+ if (req->vol_type != UBI_DYNAMIC_VOLUME &&
+ req->vol_type != UBI_STATIC_VOLUME)
+ goto bad;
+
+ if (req->alignment > ubi->leb_size)
+ goto bad;
+
+ n = req->alignment % ubi->min_io_size;
+ if (req->alignment != 1 && n)
+ goto bad;
+
+ if (req->name_len > UBI_VOL_NAME_MAX) {
+ err = -ENAMETOOLONG;
+ goto bad;
+ }
+
+ return 0;
+
+bad:
+ dbg_err("bad volume creation request");
+ ubi_dbg_dump_mkvol_req(req);
+ return err;
+}
+
+/**
+ * verify_rsvol_req - verify volume re-size request.
+ * @ubi: UBI device description object
+ * @req: the request to check
+ *
+ * This function returns zero if the request is correct, and %-EINVAL if not.
+ */
+static int verify_rsvol_req(const struct ubi_device *ubi,
+ const struct ubi_rsvol_req *req)
+{
+ if (req->bytes <= 0)
+ return -EINVAL;
+
+ if (req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct ubi_device *ubi;
+ struct ubi_volume_desc *desc;
+ void __user *argp = (void __user *)arg;
+
+ if (_IOC_NR(cmd) > UBI_CDEV_IOC_MAX_SEQ ||
+ _IOC_TYPE(cmd) != UBI_IOC_MAGIC)
+ return -ENOTTY;
+
+ if (_IOC_DIR(cmd) && _IOC_READ)
+ err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd));
+ else if (_IOC_DIR(cmd) && _IOC_WRITE)
+ err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd));
+ if (err)
+ return -EFAULT;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ ubi = major_to_device(imajor(inode));
+ if (IS_ERR(ubi))
+ return PTR_ERR(ubi);
+
+ switch (cmd) {
+ /* Create volume command */
+ case UBI_IOCMKVOL:
+ {
+ struct ubi_mkvol_req req;
+
+ dbg_msg("create volume");
+ err = __copy_from_user(&req, argp,
+ sizeof(struct ubi_mkvol_req));
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ err = verify_mkvol_req(ubi, &req);
+ if (err)
+ break;
+
+ req.name[req.name_len] = '\0';
+
+ err = ubi_create_volume(ubi, &req);
+ if (err)
+ break;
+
+ err = __put_user(req.vol_id, (__user int32_t *)argp);
+ if (err)
+ err = -EFAULT;
+
+ break;
+ }
+
+ /* Remove volume command */
+ case UBI_IOCRMVOL:
+ {
+ int vol_id;
+
+ dbg_msg("remove volume");
+ err = __get_user(vol_id, (__user int32_t *)argp);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE);
+ if (IS_ERR(desc)) {
+ err = PTR_ERR(desc);
+ break;
+ }
+
+ err = ubi_remove_volume(desc);
+ if (err)
+ ubi_close_volume(desc);
+
+ break;
+ }
+
+ /* Re-size volume command */
+ case UBI_IOCRSVOL:
+ {
+ int pebs;
+ uint64_t tmp;
+ struct ubi_rsvol_req req;
+
+ dbg_msg("re-size volume");
+ err = __copy_from_user(&req, argp,
+ sizeof(struct ubi_rsvol_req));
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ err = verify_rsvol_req(ubi, &req);
+ if (err)
+ break;
+
+ desc = ubi_open_volume(ubi->ubi_num, req.vol_id, UBI_EXCLUSIVE);
+ if (IS_ERR(desc)) {
+ err = PTR_ERR(desc);
+ break;
+ }
+
+ tmp = req.bytes;
+ pebs = !!do_div(tmp, desc->vol->usable_leb_size);
+ pebs += tmp;
+
+ err = ubi_resize_volume(desc, pebs);
+ ubi_close_volume(desc);
+ break;
+ }
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ return err;
+}
+
+/* UBI character device operations */
+struct file_operations ubi_cdev_operations = {
+ .owner = THIS_MODULE,
+ .ioctl = ubi_cdev_ioctl,
+ .llseek = no_llseek
+};
+
+/* UBI volume character device operations */
+struct file_operations ubi_vol_cdev_operations = {
+ .owner = THIS_MODULE,
+ .open = vol_cdev_open,
+ .release = vol_cdev_release,
+ .llseek = vol_cdev_llseek,
+ .read = vol_cdev_read,
+ .write = vol_cdev_write,
+ .ioctl = vol_cdev_ioctl
+};
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
new file mode 100644
index 000000000000..86364221fafe
--- /dev/null
+++ b/drivers/mtd/ubi/debug.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * Here we keep all the UBI debugging stuff which should normally be disabled
+ * and compiled-out, but it is extremely helpful when hunting bugs or doing big
+ * changes.
+ */
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+
+#include "ubi.h"
+
+/**
+ * ubi_dbg_dump_ec_hdr - dump an erase counter header.
+ * @ec_hdr: the erase counter header to dump
+ */
+void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
+{
+ dbg_msg("erase counter header dump:");
+ dbg_msg("magic %#08x", ubi32_to_cpu(ec_hdr->magic));
+ dbg_msg("version %d", (int)ec_hdr->version);
+ dbg_msg("ec %llu", (long long)ubi64_to_cpu(ec_hdr->ec));
+ dbg_msg("vid_hdr_offset %d", ubi32_to_cpu(ec_hdr->vid_hdr_offset));
+ dbg_msg("data_offset %d", ubi32_to_cpu(ec_hdr->data_offset));
+ dbg_msg("hdr_crc %#08x", ubi32_to_cpu(ec_hdr->hdr_crc));
+ dbg_msg("erase counter header hexdump:");
+ ubi_dbg_hexdump(ec_hdr, UBI_EC_HDR_SIZE);
+}
+
+/**
+ * ubi_dbg_dump_vid_hdr - dump a volume identifier header.
+ * @vid_hdr: the volume identifier header to dump
+ */
+void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
+{
+ dbg_msg("volume identifier header dump:");
+ dbg_msg("magic %08x", ubi32_to_cpu(vid_hdr->magic));
+ dbg_msg("version %d", (int)vid_hdr->version);
+ dbg_msg("vol_type %d", (int)vid_hdr->vol_type);
+ dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag);
+ dbg_msg("compat %d", (int)vid_hdr->compat);
+ dbg_msg("vol_id %d", ubi32_to_cpu(vid_hdr->vol_id));
+ dbg_msg("lnum %d", ubi32_to_cpu(vid_hdr->lnum));
+ dbg_msg("leb_ver %u", ubi32_to_cpu(vid_hdr->leb_ver));
+ dbg_msg("data_size %d", ubi32_to_cpu(vid_hdr->data_size));
+ dbg_msg("used_ebs %d", ubi32_to_cpu(vid_hdr->used_ebs));
+ dbg_msg("data_pad %d", ubi32_to_cpu(vid_hdr->data_pad));
+ dbg_msg("sqnum %llu",
+ (unsigned long long)ubi64_to_cpu(vid_hdr->sqnum));
+ dbg_msg("hdr_crc %08x", ubi32_to_cpu(vid_hdr->hdr_crc));
+ dbg_msg("volume identifier header hexdump:");
+}
+
+/**
+ * ubi_dbg_dump_vol_info- dump volume information.
+ * @vol: UBI volume description object
+ */
+void ubi_dbg_dump_vol_info(const struct ubi_volume *vol)
+{
+ dbg_msg("volume information dump:");
+ dbg_msg("vol_id %d", vol->vol_id);
+ dbg_msg("reserved_pebs %d", vol->reserved_pebs);
+ dbg_msg("alignment %d", vol->alignment);
+ dbg_msg("data_pad %d", vol->data_pad);
+ dbg_msg("vol_type %d", vol->vol_type);
+ dbg_msg("name_len %d", vol->name_len);
+ dbg_msg("usable_leb_size %d", vol->usable_leb_size);
+ dbg_msg("used_ebs %d", vol->used_ebs);
+ dbg_msg("used_bytes %lld", vol->used_bytes);
+ dbg_msg("last_eb_bytes %d", vol->last_eb_bytes);
+ dbg_msg("corrupted %d", vol->corrupted);
+ dbg_msg("upd_marker %d", vol->upd_marker);
+
+ if (vol->name_len <= UBI_VOL_NAME_MAX &&
+ strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
+ dbg_msg("name %s", vol->name);
+ } else {
+ dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c",
+ vol->name[0], vol->name[1], vol->name[2],
+ vol->name[3], vol->name[4]);
+ }
+}
+
+/**
+ * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
+ * @r: the object to dump
+ * @idx: volume table index
+ */
+void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
+{
+ int name_len = ubi16_to_cpu(r->name_len);
+
+ dbg_msg("volume table record %d dump:", idx);
+ dbg_msg("reserved_pebs %d", ubi32_to_cpu(r->reserved_pebs));
+ dbg_msg("alignment %d", ubi32_to_cpu(r->alignment));
+ dbg_msg("data_pad %d", ubi32_to_cpu(r->data_pad));
+ dbg_msg("vol_type %d", (int)r->vol_type);
+ dbg_msg("upd_marker %d", (int)r->upd_marker);
+ dbg_msg("name_len %d", name_len);
+
+ if (r->name[0] == '\0') {
+ dbg_msg("name NULL");
+ return;
+ }
+
+ if (name_len <= UBI_VOL_NAME_MAX &&
+ strnlen(&r->name[0], name_len + 1) == name_len) {
+ dbg_msg("name %s", &r->name[0]);
+ } else {
+ dbg_msg("1st 5 characters of the name: %c%c%c%c%c",
+ r->name[0], r->name[1], r->name[2], r->name[3],
+ r->name[4]);
+ }
+ dbg_msg("crc %#08x", ubi32_to_cpu(r->crc));
+}
+
+/**
+ * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object.
+ * @sv: the object to dump
+ */
+void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv)
+{
+ dbg_msg("volume scanning information dump:");
+ dbg_msg("vol_id %d", sv->vol_id);
+ dbg_msg("highest_lnum %d", sv->highest_lnum);
+ dbg_msg("leb_count %d", sv->leb_count);
+ dbg_msg("compat %d", sv->compat);
+ dbg_msg("vol_type %d", sv->vol_type);
+ dbg_msg("used_ebs %d", sv->used_ebs);
+ dbg_msg("last_data_size %d", sv->last_data_size);
+ dbg_msg("data_pad %d", sv->data_pad);
+}
+
+/**
+ * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object.
+ * @seb: the object to dump
+ * @type: object type: 0 - not corrupted, 1 - corrupted
+ */
+void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type)
+{
+ dbg_msg("eraseblock scanning information dump:");
+ dbg_msg("ec %d", seb->ec);
+ dbg_msg("pnum %d", seb->pnum);
+ if (type == 0) {
+ dbg_msg("lnum %d", seb->lnum);
+ dbg_msg("scrub %d", seb->scrub);
+ dbg_msg("sqnum %llu", seb->sqnum);
+ dbg_msg("leb_ver %u", seb->leb_ver);
+ }
+}
+
+/**
+ * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
+ * @req: the object to dump
+ */
+void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req)
+{
+ char nm[17];
+
+ dbg_msg("volume creation request dump:");
+ dbg_msg("vol_id %d", req->vol_id);
+ dbg_msg("alignment %d", req->alignment);
+ dbg_msg("bytes %lld", (long long)req->bytes);
+ dbg_msg("vol_type %d", req->vol_type);
+ dbg_msg("name_len %d", req->name_len);
+
+ memcpy(nm, req->name, 16);
+ nm[16] = 0;
+ dbg_msg("the 1st 16 characters of the name: %s", nm);
+}
+
+#define BYTES_PER_LINE 32
+
+/**
+ * ubi_dbg_hexdump - dump a buffer.
+ * @ptr: the buffer to dump
+ * @size: buffer size which must be multiple of 4 bytes
+ */
+void ubi_dbg_hexdump(const void *ptr, int size)
+{
+ int i, k = 0, rows, columns;
+ const uint8_t *p = ptr;
+
+ size = ALIGN(size, 4);
+ rows = size/BYTES_PER_LINE + size % BYTES_PER_LINE;
+ for (i = 0; i < rows; i++) {
+ int j;
+
+ cond_resched();
+ columns = min(size - k, BYTES_PER_LINE) / 4;
+ if (columns == 0)
+ break;
+ printk(KERN_DEBUG "%5d: ", i * BYTES_PER_LINE);
+ for (j = 0; j < columns; j++) {
+ int n, N;
+
+ N = size - k > 4 ? 4 : size - k;
+ for (n = 0; n < N; n++)
+ printk("%02x", p[k++]);
+ printk(" ");
+ }
+ printk("\n");
+ }
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
new file mode 100644
index 000000000000..f816ad9a36c0
--- /dev/null
+++ b/drivers/mtd/ubi/debug.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_DEBUG_H__
+#define __UBI_DEBUG_H__
+
+#ifdef CONFIG_MTD_UBI_DEBUG
+#include <linux/random.h>
+
+#define ubi_assert(expr) BUG_ON(!(expr))
+#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__)
+#else
+#define ubi_assert(expr) ({})
+#define dbg_err(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
+#define DBG_DISABLE_BGT 1
+#else
+#define DBG_DISABLE_BGT 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+/* Generic debugging message */
+#define dbg_msg(fmt, ...) \
+ printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__)
+
+#define ubi_dbg_dump_stack() dump_stack()
+
+struct ubi_ec_hdr;
+struct ubi_vid_hdr;
+struct ubi_volume;
+struct ubi_vtbl_record;
+struct ubi_scan_volume;
+struct ubi_scan_leb;
+struct ubi_mkvol_req;
+
+void ubi_dbg_print(int type, const char *func, const char *fmt, ...);
+void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr);
+void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr);
+void ubi_dbg_dump_vol_info(const struct ubi_volume *vol);
+void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx);
+void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
+void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
+void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
+void ubi_dbg_hexdump(const void *buf, int size);
+
+#else
+
+#define dbg_msg(fmt, ...) ({})
+#define ubi_dbg_dump_stack() ({})
+#define ubi_dbg_print(func, fmt, ...) ({})
+#define ubi_dbg_dump_ec_hdr(ec_hdr) ({})
+#define ubi_dbg_dump_vid_hdr(vid_hdr) ({})
+#define ubi_dbg_dump_vol_info(vol) ({})
+#define ubi_dbg_dump_vtbl_record(r, idx) ({})
+#define ubi_dbg_dump_sv(sv) ({})
+#define ubi_dbg_dump_seb(seb, type) ({})
+#define ubi_dbg_dump_mkvol_req(req) ({})
+#define ubi_dbg_hexdump(buf, size) ({})
+
+#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA
+/* Messages from the eraseblock association unit */
+#define dbg_eba(fmt, ...) \
+ printk(KERN_DEBUG "UBI DBG eba: %s: " fmt "\n", __FUNCTION__, \
+ ##__VA_ARGS__)
+#else
+#define dbg_eba(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL
+/* Messages from the wear-leveling unit */
+#define dbg_wl(fmt, ...) \
+ printk(KERN_DEBUG "UBI DBG wl: %s: " fmt "\n", __FUNCTION__, \
+ ##__VA_ARGS__)
+#else
+#define dbg_wl(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO
+/* Messages from the input/output unit */
+#define dbg_io(fmt, ...) \
+ printk(KERN_DEBUG "UBI DBG io: %s: " fmt "\n", __FUNCTION__, \
+ ##__VA_ARGS__)
+#else
+#define dbg_io(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD
+/* Initialization and build messages */
+#define dbg_bld(fmt, ...) \
+ printk(KERN_DEBUG "UBI DBG bld: %s: " fmt "\n", __FUNCTION__, \
+ ##__VA_ARGS__)
+#else
+#define dbg_bld(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS
+/**
+ * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
+ *
+ * Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
+ */
+static inline int ubi_dbg_is_bitflip(void)
+{
+ return !(random32() % 200);
+}
+#else
+#define ubi_dbg_is_bitflip() 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
+/**
+ * ubi_dbg_is_write_failure - if it is time to emulate a write failure.
+ *
+ * Returns non-zero if a write failure should be emulated, otherwise returns
+ * zero.
+ */
+static inline int ubi_dbg_is_write_failure(void)
+{
+ return !(random32() % 500);
+}
+#else
+#define ubi_dbg_is_write_failure() 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
+/**
+ * ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
+ *
+ * Returns non-zero if an erase failure should be emulated, otherwise returns
+ * zero.
+ */
+static inline int ubi_dbg_is_erase_failure(void)
+{
+ return !(random32() % 400);
+}
+#else
+#define ubi_dbg_is_erase_failure() 0
+#endif
+
+#endif /* !__UBI_DEBUG_H__ */
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
new file mode 100644
index 000000000000..d847ee1da3d9
--- /dev/null
+++ b/drivers/mtd/ubi/eba.c
@@ -0,0 +1,1241 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * The UBI Eraseblock Association (EBA) unit.
+ *
+ * This unit is responsible for I/O to/from logical eraseblock.
+ *
+ * Although in this implementation the EBA table is fully kept and managed in
+ * RAM, which assumes poor scalability, it might be (partially) maintained on
+ * flash in future implementations.
+ *
+ * The EBA unit implements per-logical eraseblock locking. Before accessing a
+ * logical eraseblock it is locked for reading or writing. The per-logical
+ * eraseblock locking is implemented by means of the lock tree. The lock tree
+ * is an RB-tree which refers all the currently locked logical eraseblocks. The
+ * lock tree elements are &struct ltree_entry objects. They are indexed by
+ * (@vol_id, @lnum) pairs.
+ *
+ * EBA also maintains the global sequence counter which is incremented each
+ * time a logical eraseblock is mapped to a physical eraseblock and it is
+ * stored in the volume identifier header. This means that each VID header has
+ * a unique sequence number. The sequence number is only increased an we assume
+ * 64 bits is enough to never overflow.
+ */
+
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/err.h>
+#include "ubi.h"
+
+/**
+ * struct ltree_entry - an entry in the lock tree.
+ * @rb: links RB-tree nodes
+ * @vol_id: volume ID of the locked logical eraseblock
+ * @lnum: locked logical eraseblock number
+ * @users: how many tasks are using this logical eraseblock or wait for it
+ * @mutex: read/write mutex to implement read/write access serialization to
+ * the (@vol_id, @lnum) logical eraseblock
+ *
+ * When a logical eraseblock is being locked - corresponding &struct ltree_entry
+ * object is inserted to the lock tree (@ubi->ltree).
+ */
+struct ltree_entry {
+ struct rb_node rb;
+ int vol_id;
+ int lnum;
+ int users;
+ struct rw_semaphore mutex;
+};
+
+/* Slab cache for lock-tree entries */
+static struct kmem_cache *ltree_slab;
+
+/**
+ * next_sqnum - get next sequence number.
+ * @ubi: UBI device description object
+ *
+ * This function returns next sequence number to use, which is just the current
+ * global sequence counter value. It also increases the global sequence
+ * counter.
+ */
+static unsigned long long next_sqnum(struct ubi_device *ubi)
+{
+ unsigned long long sqnum;
+
+ spin_lock(&ubi->ltree_lock);
+ sqnum = ubi->global_sqnum++;
+ spin_unlock(&ubi->ltree_lock);
+
+ return sqnum;
+}
+
+/**
+ * ubi_get_compat - get compatibility flags of a volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function returns compatibility flags for an internal volume. User
+ * volumes have no compatibility flags, so %0 is returned.
+ */
+static int ubi_get_compat(const struct ubi_device *ubi, int vol_id)
+{
+ if (vol_id == UBI_LAYOUT_VOL_ID)
+ return UBI_LAYOUT_VOLUME_COMPAT;
+ return 0;
+}
+
+/**
+ * ltree_lookup - look up the lock tree.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function returns a pointer to the corresponding &struct ltree_entry
+ * object if the logical eraseblock is locked and %NULL if it is not.
+ * @ubi->ltree_lock has to be locked.
+ */
+static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id,
+ int lnum)
+{
+ struct rb_node *p;
+
+ p = ubi->ltree.rb_node;
+ while (p) {
+ struct ltree_entry *le;
+
+ le = rb_entry(p, struct ltree_entry, rb);
+
+ if (vol_id < le->vol_id)
+ p = p->rb_left;
+ else if (vol_id > le->vol_id)
+ p = p->rb_right;
+ else {
+ if (lnum < le->lnum)
+ p = p->rb_left;
+ else if (lnum > le->lnum)
+ p = p->rb_right;
+ else
+ return le;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * ltree_add_entry - add new entry to the lock tree.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function adds new entry for logical eraseblock (@vol_id, @lnum) to the
+ * lock tree. If such entry is already there, its usage counter is increased.
+ * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation
+ * failed.
+ */
+static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id,
+ int lnum)
+{
+ struct ltree_entry *le, *le1, *le_free;
+
+ le = kmem_cache_alloc(ltree_slab, GFP_KERNEL);
+ if (!le)
+ return ERR_PTR(-ENOMEM);
+
+ le->vol_id = vol_id;
+ le->lnum = lnum;
+
+ spin_lock(&ubi->ltree_lock);
+ le1 = ltree_lookup(ubi, vol_id, lnum);
+
+ if (le1) {
+ /*
+ * This logical eraseblock is already locked. The newly
+ * allocated lock entry is not needed.
+ */
+ le_free = le;
+ le = le1;
+ } else {
+ struct rb_node **p, *parent = NULL;
+
+ /*
+ * No lock entry, add the newly allocated one to the
+ * @ubi->ltree RB-tree.
+ */
+ le_free = NULL;
+
+ p = &ubi->ltree.rb_node;
+ while (*p) {
+ parent = *p;
+ le1 = rb_entry(parent, struct ltree_entry, rb);
+
+ if (vol_id < le1->vol_id)
+ p = &(*p)->rb_left;
+ else if (vol_id > le1->vol_id)
+ p = &(*p)->rb_right;
+ else {
+ ubi_assert(lnum != le1->lnum);
+ if (lnum < le1->lnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ }
+
+ rb_link_node(&le->rb, parent, p);
+ rb_insert_color(&le->rb, &ubi->ltree);
+ }
+ le->users += 1;
+ spin_unlock(&ubi->ltree_lock);
+
+ if (le_free)
+ kmem_cache_free(ltree_slab, le_free);
+
+ return le;
+}
+
+/**
+ * leb_read_lock - lock logical eraseblock for reading.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for reading. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ struct ltree_entry *le;
+
+ le = ltree_add_entry(ubi, vol_id, lnum);
+ if (IS_ERR(le))
+ return PTR_ERR(le);
+ down_read(&le->mutex);
+ return 0;
+}
+
+/**
+ * leb_read_unlock - unlock logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ */
+static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ int free = 0;
+ struct ltree_entry *le;
+
+ spin_lock(&ubi->ltree_lock);
+ le = ltree_lookup(ubi, vol_id, lnum);
+ le->users -= 1;
+ ubi_assert(le->users >= 0);
+ if (le->users == 0) {
+ rb_erase(&le->rb, &ubi->ltree);
+ free = 1;
+ }
+ spin_unlock(&ubi->ltree_lock);
+
+ up_read(&le->mutex);
+ if (free)
+ kmem_cache_free(ltree_slab, le);
+}
+
+/**
+ * leb_write_lock - lock logical eraseblock for writing.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function locks a logical eraseblock for writing. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ struct ltree_entry *le;
+
+ le = ltree_add_entry(ubi, vol_id, lnum);
+ if (IS_ERR(le))
+ return PTR_ERR(le);
+ down_write(&le->mutex);
+ return 0;
+}
+
+/**
+ * leb_write_unlock - unlock logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ */
+static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ int free;
+ struct ltree_entry *le;
+
+ spin_lock(&ubi->ltree_lock);
+ le = ltree_lookup(ubi, vol_id, lnum);
+ le->users -= 1;
+ ubi_assert(le->users >= 0);
+ if (le->users == 0) {
+ rb_erase(&le->rb, &ubi->ltree);
+ free = 1;
+ } else
+ free = 0;
+ spin_unlock(&ubi->ltree_lock);
+
+ up_write(&le->mutex);
+ if (free)
+ kmem_cache_free(ltree_slab, le);
+}
+
+/**
+ * ubi_eba_unmap_leb - un-map logical eraseblock.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ *
+ * This function un-maps logical eraseblock @lnum and schedules corresponding
+ * physical eraseblock for erasure. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum)
+{
+ int idx = vol_id2idx(ubi, vol_id), err, pnum;
+ struct ubi_volume *vol = ubi->volumes[idx];
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err)
+ return err;
+
+ pnum = vol->eba_tbl[lnum];
+ if (pnum < 0)
+ /* This logical eraseblock is already unmapped */
+ goto out_unlock;
+
+ dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum);
+
+ vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED;
+ err = ubi_wl_put_peb(ubi, pnum, 0);
+
+out_unlock:
+ leb_write_unlock(ubi, vol_id, lnum);
+ return err;
+}
+
+/**
+ * ubi_eba_read_leb - read data.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: buffer to store the read data
+ * @offset: offset from where to read
+ * @len: how many bytes to read
+ * @check: data CRC check flag
+ *
+ * If the logical eraseblock @lnum is unmapped, @buf is filled with 0xFF
+ * bytes. The @check flag only makes sense for static volumes and forces
+ * eraseblock data CRC checking.
+ *
+ * In case of success this function returns zero. In case of a static volume,
+ * if data CRC mismatches - %-EBADMSG is returned. %-EBADMSG may also be
+ * returned for any volume type if an ECC error was detected by the MTD device
+ * driver. Other negative error cored may be returned in case of other errors.
+ */
+int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf,
+ int offset, int len, int check)
+{
+ int err, pnum, scrub = 0, idx = vol_id2idx(ubi, vol_id);
+ struct ubi_vid_hdr *vid_hdr;
+ struct ubi_volume *vol = ubi->volumes[idx];
+ uint32_t crc, crc1;
+
+ err = leb_read_lock(ubi, vol_id, lnum);
+ if (err)
+ return err;
+
+ pnum = vol->eba_tbl[lnum];
+ if (pnum < 0) {
+ /*
+ * The logical eraseblock is not mapped, fill the whole buffer
+ * with 0xFF bytes. The exception is static volumes for which
+ * it is an error to read unmapped logical eraseblocks.
+ */
+ dbg_eba("read %d bytes from offset %d of LEB %d:%d (unmapped)",
+ len, offset, vol_id, lnum);
+ leb_read_unlock(ubi, vol_id, lnum);
+ ubi_assert(vol->vol_type != UBI_STATIC_VOLUME);
+ memset(buf, 0xFF, len);
+ return 0;
+ }
+
+ dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d",
+ len, offset, vol_id, lnum, pnum);
+
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+ check = 0;
+
+retry:
+ if (check) {
+ vid_hdr = ubi_zalloc_vid_hdr(ubi);
+ if (!vid_hdr) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
+ if (err && err != UBI_IO_BITFLIPS) {
+ if (err > 0) {
+ /*
+ * The header is either absent or corrupted.
+ * The former case means there is a bug -
+ * switch to read-only mode just in case.
+ * The latter case means a real corruption - we
+ * may try to recover data. FIXME: but this is
+ * not implemented.
+ */
+ if (err == UBI_IO_BAD_VID_HDR) {
+ ubi_warn("bad VID header at PEB %d, LEB"
+ "%d:%d", pnum, vol_id, lnum);
+ err = -EBADMSG;
+ } else
+ ubi_ro_mode(ubi);
+ }
+ goto out_free;
+ } else if (err == UBI_IO_BITFLIPS)
+ scrub = 1;
+
+ ubi_assert(lnum < ubi32_to_cpu(vid_hdr->used_ebs));
+ ubi_assert(len == ubi32_to_cpu(vid_hdr->data_size));
+
+ crc = ubi32_to_cpu(vid_hdr->data_crc);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ }
+
+ err = ubi_io_read_data(ubi, buf, pnum, offset, len);
+ if (err) {
+ if (err == UBI_IO_BITFLIPS) {
+ scrub = 1;
+ err = 0;
+ } else if (err == -EBADMSG) {
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+ goto out_unlock;
+ scrub = 1;
+ if (!check) {
+ ubi_msg("force data checking");
+ check = 1;
+ goto retry;
+ }
+ } else
+ goto out_unlock;
+ }
+
+ if (check) {
+ crc1 = crc32(UBI_CRC32_INIT, buf, len);
+ if (crc1 != crc) {
+ ubi_warn("CRC error: calculated %#08x, must be %#08x",
+ crc1, crc);
+ err = -EBADMSG;
+ goto out_unlock;
+ }
+ }
+
+ if (scrub)
+ err = ubi_wl_scrub_peb(ubi, pnum);
+
+ leb_read_unlock(ubi, vol_id, lnum);
+ return err;
+
+out_free:
+ ubi_free_vid_hdr(ubi, vid_hdr);
+out_unlock:
+ leb_read_unlock(ubi, vol_id, lnum);
+ return err;
+}
+
+/**
+ * recover_peb - recover from write failure.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to recover
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data which was not written because of the write failure
+ * @offset: offset of the failed write
+ * @len: how many bytes should have been written
+ *
+ * This function is called in case of a write failure and moves all good data
+ * from the potentially bad physical eraseblock to a good physical eraseblock.
+ * This function also writes the data which was not written due to the failure.
+ * Returns new physical eraseblock number in case of success, and a negative
+ * error code in case of failure.
+ */
+static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
+ const void *buf, int offset, int len)
+{
+ int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0;
+ struct ubi_volume *vol = ubi->volumes[idx];
+ struct ubi_vid_hdr *vid_hdr;
+ unsigned char *new_buf;
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi);
+ if (!vid_hdr) {
+ return -ENOMEM;
+ }
+
+retry:
+ new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN);
+ if (new_pnum < 0) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return new_pnum;
+ }
+
+ ubi_msg("recover PEB %d, move data to PEB %d", pnum, new_pnum);
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1);
+ if (err && err != UBI_IO_BITFLIPS) {
+ if (err > 0)
+ err = -EIO;
+ goto out_put;
+ }
+
+ vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+ err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
+ if (err)
+ goto write_error;
+
+ data_size = offset + len;
+ new_buf = kmalloc(data_size, GFP_KERNEL);
+ if (!new_buf) {
+ err = -ENOMEM;
+ goto out_put;
+ }
+ memset(new_buf + offset, 0xFF, len);
+
+ /* Read everything before the area where the write failure happened */
+ if (offset > 0) {
+ err = ubi_io_read_data(ubi, new_buf, pnum, 0, offset);
+ if (err && err != UBI_IO_BITFLIPS) {
+ kfree(new_buf);
+ goto out_put;
+ }
+ }
+
+ memcpy(new_buf + offset, buf, len);
+
+ err = ubi_io_write_data(ubi, new_buf, new_pnum, 0, data_size);
+ if (err) {
+ kfree(new_buf);
+ goto write_error;
+ }
+
+ kfree(new_buf);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+
+ vol->eba_tbl[lnum] = new_pnum;
+ ubi_wl_put_peb(ubi, pnum, 1);
+
+ ubi_msg("data was successfully recovered");
+ return 0;
+
+out_put:
+ ubi_wl_put_peb(ubi, new_pnum, 1);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+
+write_error:
+ /*
+ * Bad luck? This physical eraseblock is bad too? Crud. Let's try to
+ * get another one.
+ */
+ ubi_warn("failed to write to PEB %d", new_pnum);
+ ubi_wl_put_peb(ubi, new_pnum, 1);
+ if (++tries > UBI_IO_RETRIES) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+ ubi_msg("try again");
+ goto retry;
+}
+
+/**
+ * ubi_eba_write_leb - write data to dynamic volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: the data to write
+ * @offset: offset within the logical eraseblock where to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ *
+ * This function writes data to logical eraseblock @lnum of a dynamic volume
+ * @vol_id. Returns zero in case of success and a negative error code in case
+ * of failure. In case of error, it is possible that something was still
+ * written to the flash media, but may be some garbage.
+ */
+int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum,
+ const void *buf, int offset, int len, int dtype)
+{
+ int idx = vol_id2idx(ubi, vol_id), err, pnum, tries = 0;
+ struct ubi_volume *vol = ubi->volumes[idx];
+ struct ubi_vid_hdr *vid_hdr;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err)
+ return err;
+
+ pnum = vol->eba_tbl[lnum];
+ if (pnum >= 0) {
+ dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d",
+ len, offset, vol_id, lnum, pnum);
+
+ err = ubi_io_write_data(ubi, buf, pnum, offset, len);
+ if (err) {
+ ubi_warn("failed to write data to PEB %d", pnum);
+ if (err == -EIO && ubi->bad_allowed)
+ err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len);
+ if (err)
+ ubi_ro_mode(ubi);
+ }
+ leb_write_unlock(ubi, vol_id, lnum);
+ return err;
+ }
+
+ /*
+ * The logical eraseblock is not mapped. We have to get a free physical
+ * eraseblock and write the volume identifier header there first.
+ */
+ vid_hdr = ubi_zalloc_vid_hdr(ubi);
+ if (!vid_hdr) {
+ leb_write_unlock(ubi, vol_id, lnum);
+ return -ENOMEM;
+ }
+
+ vid_hdr->vol_type = UBI_VID_DYNAMIC;
+ vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+ vid_hdr->vol_id = cpu_to_ubi32(vol_id);
+ vid_hdr->lnum = cpu_to_ubi32(lnum);
+ vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+ vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad);
+
+retry:
+ pnum = ubi_wl_get_peb(ubi, dtype);
+ if (pnum < 0) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ leb_write_unlock(ubi, vol_id, lnum);
+ return pnum;
+ }
+
+ dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d",
+ len, offset, vol_id, lnum, pnum);
+
+ err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+ if (err) {
+ ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+ vol_id, lnum, pnum);
+ goto write_error;
+ }
+
+ err = ubi_io_write_data(ubi, buf, pnum, offset, len);
+ if (err) {
+ ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, "
+ "PEB %d", len, offset, vol_id, lnum, pnum);
+ goto write_error;
+ }
+
+ vol->eba_tbl[lnum] = pnum;
+
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return 0;
+
+write_error:
+ if (err != -EIO || !ubi->bad_allowed) {
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ /*
+ * Fortunately, this is the first write operation to this physical
+ * eraseblock, so just put it and request a new one. We assume that if
+ * this physical eraseblock went bad, the erase code will handle that.
+ */
+ err = ubi_wl_put_peb(ubi, pnum, 1);
+ if (err || ++tries > UBI_IO_RETRIES) {
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+ ubi_msg("try another PEB");
+ goto retry;
+}
+
+/**
+ * ubi_eba_write_leb_st - write data to static volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ * @used_ebs: how many logical eraseblocks will this volume contain
+ *
+ * This function writes data to logical eraseblock @lnum of static volume
+ * @vol_id. The @used_ebs argument should contain total number of logical
+ * eraseblock in this static volume.
+ *
+ * When writing to the last logical eraseblock, the @len argument doesn't have
+ * to be aligned to the minimal I/O unit size. Instead, it has to be equivalent
+ * to the real data size, although the @buf buffer has to contain the
+ * alignment. In all other cases, @len has to be aligned.
+ *
+ * It is prohibited to write more then once to logical eraseblocks of static
+ * volumes. This function returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum,
+ const void *buf, int len, int dtype, int used_ebs)
+{
+ int err, pnum, tries = 0, data_size = len;
+ int idx = vol_id2idx(ubi, vol_id);
+ struct ubi_volume *vol = ubi->volumes[idx];
+ struct ubi_vid_hdr *vid_hdr;
+ uint32_t crc;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ if (lnum == used_ebs - 1)
+ /* If this is the last LEB @len may be unaligned */
+ len = ALIGN(data_size, ubi->min_io_size);
+ else
+ ubi_assert(len % ubi->min_io_size == 0);
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+ vid_hdr->vol_id = cpu_to_ubi32(vol_id);
+ vid_hdr->lnum = cpu_to_ubi32(lnum);
+ vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+ vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad);
+
+ crc = crc32(UBI_CRC32_INIT, buf, data_size);
+ vid_hdr->vol_type = UBI_VID_STATIC;
+ vid_hdr->data_size = cpu_to_ubi32(data_size);
+ vid_hdr->used_ebs = cpu_to_ubi32(used_ebs);
+ vid_hdr->data_crc = cpu_to_ubi32(crc);
+
+retry:
+ pnum = ubi_wl_get_peb(ubi, dtype);
+ if (pnum < 0) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ leb_write_unlock(ubi, vol_id, lnum);
+ return pnum;
+ }
+
+ dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d",
+ len, vol_id, lnum, pnum, used_ebs);
+
+ err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+ if (err) {
+ ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+ vol_id, lnum, pnum);
+ goto write_error;
+ }
+
+ err = ubi_io_write_data(ubi, buf, pnum, 0, len);
+ if (err) {
+ ubi_warn("failed to write %d bytes of data to PEB %d",
+ len, pnum);
+ goto write_error;
+ }
+
+ ubi_assert(vol->eba_tbl[lnum] < 0);
+ vol->eba_tbl[lnum] = pnum;
+
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return 0;
+
+write_error:
+ if (err != -EIO || !ubi->bad_allowed) {
+ /*
+ * This flash device does not admit of bad eraseblocks or
+ * something nasty and unexpected happened. Switch to read-only
+ * mode just in case.
+ */
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ err = ubi_wl_put_peb(ubi, pnum, 1);
+ if (err || ++tries > UBI_IO_RETRIES) {
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+ ubi_msg("try another PEB");
+ goto retry;
+}
+
+/*
+ * ubi_eba_atomic_leb_change - change logical eraseblock atomically.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: data type
+ *
+ * This function changes the contents of a logical eraseblock atomically. @buf
+ * has to contain new logical eraseblock data, and @len - the length of the
+ * data, which has to be aligned. This function guarantees that in case of an
+ * unclean reboot the old contents is preserved. Returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum,
+ const void *buf, int len, int dtype)
+{
+ int err, pnum, tries = 0, idx = vol_id2idx(ubi, vol_id);
+ struct ubi_volume *vol = ubi->volumes[idx];
+ struct ubi_vid_hdr *vid_hdr;
+ uint32_t crc;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+ vid_hdr->vol_id = cpu_to_ubi32(vol_id);
+ vid_hdr->lnum = cpu_to_ubi32(lnum);
+ vid_hdr->compat = ubi_get_compat(ubi, vol_id);
+ vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad);
+
+ crc = crc32(UBI_CRC32_INIT, buf, len);
+ vid_hdr->vol_type = UBI_VID_STATIC;
+ vid_hdr->data_size = cpu_to_ubi32(len);
+ vid_hdr->copy_flag = 1;
+ vid_hdr->data_crc = cpu_to_ubi32(crc);
+
+retry:
+ pnum = ubi_wl_get_peb(ubi, dtype);
+ if (pnum < 0) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ leb_write_unlock(ubi, vol_id, lnum);
+ return pnum;
+ }
+
+ dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d",
+ vol_id, lnum, vol->eba_tbl[lnum], pnum);
+
+ err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr);
+ if (err) {
+ ubi_warn("failed to write VID header to LEB %d:%d, PEB %d",
+ vol_id, lnum, pnum);
+ goto write_error;
+ }
+
+ err = ubi_io_write_data(ubi, buf, pnum, 0, len);
+ if (err) {
+ ubi_warn("failed to write %d bytes of data to PEB %d",
+ len, pnum);
+ goto write_error;
+ }
+
+ err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1);
+ if (err) {
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ leb_write_unlock(ubi, vol_id, lnum);
+ return err;
+ }
+
+ vol->eba_tbl[lnum] = pnum;
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return 0;
+
+write_error:
+ if (err != -EIO || !ubi->bad_allowed) {
+ /*
+ * This flash device does not admit of bad eraseblocks or
+ * something nasty and unexpected happened. Switch to read-only
+ * mode just in case.
+ */
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ err = ubi_wl_put_peb(ubi, pnum, 1);
+ if (err || ++tries > UBI_IO_RETRIES) {
+ ubi_ro_mode(ubi);
+ leb_write_unlock(ubi, vol_id, lnum);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+ }
+
+ vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+ ubi_msg("try another PEB");
+ goto retry;
+}
+
+/**
+ * ltree_entry_ctor - lock tree entries slab cache constructor.
+ * @obj: the lock-tree entry to construct
+ * @cache: the lock tree entry slab cache
+ * @flags: constructor flags
+ */
+static void ltree_entry_ctor(void *obj, struct kmem_cache *cache,
+ unsigned long flags)
+{
+ struct ltree_entry *le = obj;
+
+ if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) !=
+ SLAB_CTOR_CONSTRUCTOR)
+ return;
+
+ le->users = 0;
+ init_rwsem(&le->mutex);
+}
+
+/**
+ * ubi_eba_copy_leb - copy logical eraseblock.
+ * @ubi: UBI device description object
+ * @from: physical eraseblock number from where to copy
+ * @to: physical eraseblock number where to copy
+ * @vid_hdr: VID header of the @from physical eraseblock
+ *
+ * This function copies logical eraseblock from physical eraseblock @from to
+ * physical eraseblock @to. The @vid_hdr buffer may be changed by this
+ * function. Returns zero in case of success, %UBI_IO_BITFLIPS if the operation
+ * was canceled because bit-flips were detected at the target PEB, and a
+ * negative error code in case of failure.
+ */
+int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
+ struct ubi_vid_hdr *vid_hdr)
+{
+ int err, vol_id, lnum, data_size, aldata_size, pnum, idx;
+ struct ubi_volume *vol;
+ uint32_t crc;
+ void *buf, *buf1 = NULL;
+
+ vol_id = ubi32_to_cpu(vid_hdr->vol_id);
+ lnum = ubi32_to_cpu(vid_hdr->lnum);
+
+ dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to);
+
+ if (vid_hdr->vol_type == UBI_VID_STATIC) {
+ data_size = ubi32_to_cpu(vid_hdr->data_size);
+ aldata_size = ALIGN(data_size, ubi->min_io_size);
+ } else
+ data_size = aldata_size =
+ ubi->leb_size - ubi32_to_cpu(vid_hdr->data_pad);
+
+ buf = kmalloc(aldata_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /*
+ * We do not want anybody to write to this logical eraseblock while we
+ * are moving it, so we lock it.
+ */
+ err = leb_write_lock(ubi, vol_id, lnum);
+ if (err) {
+ kfree(buf);
+ return err;
+ }
+
+ /*
+ * But the logical eraseblock might have been put by this time.
+ * Cancel if it is true.
+ */
+ idx = vol_id2idx(ubi, vol_id);
+
+ /*
+ * We may race with volume deletion/re-size, so we have to hold
+ * @ubi->volumes_lock.
+ */
+ spin_lock(&ubi->volumes_lock);
+ vol = ubi->volumes[idx];
+ if (!vol) {
+ dbg_eba("volume %d was removed meanwhile", vol_id);
+ spin_unlock(&ubi->volumes_lock);
+ goto out_unlock;
+ }
+
+ pnum = vol->eba_tbl[lnum];
+ if (pnum != from) {
+ dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to "
+ "PEB %d, cancel", vol_id, lnum, from, pnum);
+ spin_unlock(&ubi->volumes_lock);
+ goto out_unlock;
+ }
+ spin_unlock(&ubi->volumes_lock);
+
+ /* OK, now the LEB is locked and we can safely start moving it */
+
+ dbg_eba("read %d bytes of data", aldata_size);
+ err = ubi_io_read_data(ubi, buf, from, 0, aldata_size);
+ if (err && err != UBI_IO_BITFLIPS) {
+ ubi_warn("error %d while reading data from PEB %d",
+ err, from);
+ goto out_unlock;
+ }
+
+ /*
+ * Now we have got to calculate how much data we have to to copy. In
+ * case of a static volume it is fairly easy - the VID header contains
+ * the data size. In case of a dynamic volume it is more difficult - we
+ * have to read the contents, cut 0xFF bytes from the end and copy only
+ * the first part. We must do this to avoid writing 0xFF bytes as it
+ * may have some side-effects. And not only this. It is important not
+ * to include those 0xFFs to CRC because later the they may be filled
+ * by data.
+ */
+ if (vid_hdr->vol_type == UBI_VID_DYNAMIC)
+ aldata_size = data_size =
+ ubi_calc_data_len(ubi, buf, data_size);
+
+ cond_resched();
+ crc = crc32(UBI_CRC32_INIT, buf, data_size);
+ cond_resched();
+
+ /*
+ * It may turn out to me that the whole @from physical eraseblock
+ * contains only 0xFF bytes. Then we have to only write the VID header
+ * and do not write any data. This also means we should not set
+ * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc.
+ */
+ if (data_size > 0) {
+ vid_hdr->copy_flag = 1;
+ vid_hdr->data_size = cpu_to_ubi32(data_size);
+ vid_hdr->data_crc = cpu_to_ubi32(crc);
+ }
+ vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi));
+
+ err = ubi_io_write_vid_hdr(ubi, to, vid_hdr);
+ if (err)
+ goto out_unlock;
+
+ cond_resched();
+
+ /* Read the VID header back and check if it was written correctly */
+ err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1);
+ if (err) {
+ if (err != UBI_IO_BITFLIPS)
+ ubi_warn("cannot read VID header back from PEB %d", to);
+ goto out_unlock;
+ }
+
+ if (data_size > 0) {
+ err = ubi_io_write_data(ubi, buf, to, 0, aldata_size);
+ if (err)
+ goto out_unlock;
+
+ /*
+ * We've written the data and are going to read it back to make
+ * sure it was written correctly.
+ */
+ buf1 = kmalloc(aldata_size, GFP_KERNEL);
+ if (!buf1) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ cond_resched();
+
+ err = ubi_io_read_data(ubi, buf1, to, 0, aldata_size);
+ if (err) {
+ if (err != UBI_IO_BITFLIPS)
+ ubi_warn("cannot read data back from PEB %d",
+ to);
+ goto out_unlock;
+ }
+
+ cond_resched();
+
+ if (memcmp(buf, buf1, aldata_size)) {
+ ubi_warn("read data back from PEB %d - it is different",
+ to);
+ goto out_unlock;
+ }
+ }
+
+ ubi_assert(vol->eba_tbl[lnum] == from);
+ vol->eba_tbl[lnum] = to;
+
+ leb_write_unlock(ubi, vol_id, lnum);
+ kfree(buf);
+ kfree(buf1);
+
+ return 0;
+
+out_unlock:
+ leb_write_unlock(ubi, vol_id, lnum);
+ kfree(buf);
+ kfree(buf1);
+ return err;
+}
+
+/**
+ * ubi_eba_init_scan - initialize the EBA unit using scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+ int i, j, err, num_volumes;
+ struct ubi_scan_volume *sv;
+ struct ubi_volume *vol;
+ struct ubi_scan_leb *seb;
+ struct rb_node *rb;
+
+ dbg_eba("initialize EBA unit");
+
+ spin_lock_init(&ubi->ltree_lock);
+ ubi->ltree = RB_ROOT;
+
+ if (ubi_devices_cnt == 0) {
+ ltree_slab = kmem_cache_create("ubi_ltree_slab",
+ sizeof(struct ltree_entry), 0,
+ 0, &ltree_entry_ctor, NULL);
+ if (!ltree_slab)
+ return -ENOMEM;
+ }
+
+ ubi->global_sqnum = si->max_sqnum + 1;
+ num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
+
+ for (i = 0; i < num_volumes; i++) {
+ vol = ubi->volumes[i];
+ if (!vol)
+ continue;
+
+ cond_resched();
+
+ vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int),
+ GFP_KERNEL);
+ if (!vol->eba_tbl) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ for (j = 0; j < vol->reserved_pebs; j++)
+ vol->eba_tbl[j] = UBI_LEB_UNMAPPED;
+
+ sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i));
+ if (!sv)
+ continue;
+
+ ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
+ if (seb->lnum >= vol->reserved_pebs)
+ /*
+ * This may happen in case of an unclean reboot
+ * during re-size.
+ */
+ ubi_scan_move_to_list(sv, seb, &si->erase);
+ vol->eba_tbl[seb->lnum] = seb->pnum;
+ }
+ }
+
+ if (ubi->bad_allowed) {
+ ubi_calculate_reserved(ubi);
+
+ if (ubi->avail_pebs < ubi->beb_rsvd_level) {
+ /* No enough free physical eraseblocks */
+ ubi->beb_rsvd_pebs = ubi->avail_pebs;
+ ubi_warn("cannot reserve enough PEBs for bad PEB "
+ "handling, reserved %d, need %d",
+ ubi->beb_rsvd_pebs, ubi->beb_rsvd_level);
+ } else
+ ubi->beb_rsvd_pebs = ubi->beb_rsvd_level;
+
+ ubi->avail_pebs -= ubi->beb_rsvd_pebs;
+ ubi->rsvd_pebs += ubi->beb_rsvd_pebs;
+ }
+
+ dbg_eba("EBA unit is initialized");
+ return 0;
+
+out_free:
+ for (i = 0; i < num_volumes; i++) {
+ if (!ubi->volumes[i])
+ continue;
+ kfree(ubi->volumes[i]->eba_tbl);
+ }
+ if (ubi_devices_cnt == 0)
+ kmem_cache_destroy(ltree_slab);
+ return err;
+}
+
+/**
+ * ubi_eba_close - close EBA unit.
+ * @ubi: UBI device description object
+ */
+void ubi_eba_close(const struct ubi_device *ubi)
+{
+ int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
+
+ dbg_eba("close EBA unit");
+
+ for (i = 0; i < num_volumes; i++) {
+ if (!ubi->volumes[i])
+ continue;
+ kfree(ubi->volumes[i]->eba_tbl);
+ }
+ if (ubi_devices_cnt == 1)
+ kmem_cache_destroy(ltree_slab);
+}
diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c
new file mode 100644
index 000000000000..fc9478d605ff
--- /dev/null
+++ b/drivers/mtd/ubi/gluebi.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel
+ */
+
+/*
+ * This file includes implementation of fake MTD devices for each UBI volume.
+ * This sounds strange, but it is in fact quite useful to make MTD-oriented
+ * software (including all the legacy software) to work on top of UBI.
+ *
+ * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit
+ * size (mtd->writesize) is equivalent to the UBI minimal I/O unit. The
+ * eraseblock size is equivalent to the logical eraseblock size of the volume.
+ */
+
+#include <asm/div64.h>
+#include "ubi.h"
+
+/**
+ * gluebi_get_device - get MTD device reference.
+ * @mtd: the MTD device description object
+ *
+ * This function is called every time the MTD device is being opened and
+ * implements the MTD get_device() operation. Returns zero in case of success
+ * and a negative error code in case of failure.
+ */
+static int gluebi_get_device(struct mtd_info *mtd)
+{
+ struct ubi_volume *vol;
+
+ vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+
+ /*
+ * We do not introduce locks for gluebi reference count because the
+ * get_device()/put_device() calls are already serialized at MTD.
+ */
+ if (vol->gluebi_refcount > 0) {
+ /*
+ * The MTD device is already referenced and this is just one
+ * more reference. MTD allows many users to open the same
+ * volume simultaneously and do not distinguish between
+ * readers/writers/exclusive openers as UBI does. So we do not
+ * open the UBI volume again - just increase the reference
+ * counter and return.
+ */
+ vol->gluebi_refcount += 1;
+ return 0;
+ }
+
+ /*
+ * This is the first reference to this UBI volume via the MTD device
+ * interface. Open the corresponding volume in read-write mode.
+ */
+ vol->gluebi_desc = ubi_open_volume(vol->ubi->ubi_num, vol->vol_id,
+ UBI_READWRITE);
+ if (IS_ERR(vol->gluebi_desc))
+ return PTR_ERR(vol->gluebi_desc);
+ vol->gluebi_refcount += 1;
+ return 0;
+}
+
+/**
+ * gluebi_put_device - put MTD device reference.
+ * @mtd: the MTD device description object
+ *
+ * This function is called every time the MTD device is being put. Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static void gluebi_put_device(struct mtd_info *mtd)
+{
+ struct ubi_volume *vol;
+
+ vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+ vol->gluebi_refcount -= 1;
+ ubi_assert(vol->gluebi_refcount >= 0);
+ if (vol->gluebi_refcount == 0)
+ ubi_close_volume(vol->gluebi_desc);
+}
+
+/**
+ * gluebi_read - read operation of emulated MTD devices.
+ * @mtd: MTD device description object
+ * @from: absolute offset from where to read
+ * @len: how many bytes to read
+ * @retlen: count of read bytes is returned here
+ * @buf: buffer to store the read data
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, unsigned char *buf)
+{
+ int err = 0, lnum, offs, total_read;
+ struct ubi_volume *vol;
+ struct ubi_device *ubi;
+ uint64_t tmp = from;
+
+ dbg_msg("read %zd bytes from offset %lld", len, from);
+
+ if (len < 0 || from < 0 || from + len > mtd->size)
+ return -EINVAL;
+
+ vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+ ubi = vol->ubi;
+
+ offs = do_div(tmp, mtd->erasesize);
+ lnum = tmp;
+
+ total_read = len;
+ while (total_read) {
+ size_t to_read = mtd->erasesize - offs;
+
+ if (to_read > total_read)
+ to_read = total_read;
+
+ err = ubi_eba_read_leb(ubi, vol->vol_id, lnum, buf, offs,
+ to_read, 0);
+ if (err)
+ break;
+
+ lnum += 1;
+ offs = 0;
+ total_read -= to_read;
+ buf += to_read;
+ }
+
+ *retlen = len - total_read;
+ return err;
+}
+
+/**
+ * gluebi_write - write operation of emulated MTD devices.
+ * @mtd: MTD device description object
+ * @to: absolute offset where to write
+ * @len: how many bytes to write
+ * @retlen: count of written bytes is returned here
+ * @buf: buffer with data to write
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ int err = 0, lnum, offs, total_written;
+ struct ubi_volume *vol;
+ struct ubi_device *ubi;
+ uint64_t tmp = to;
+
+ dbg_msg("write %zd bytes to offset %lld", len, to);
+
+ if (len < 0 || to < 0 || len + to > mtd->size)
+ return -EINVAL;
+
+ vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+ ubi = vol->ubi;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ offs = do_div(tmp, mtd->erasesize);
+ lnum = tmp;
+
+ if (len % mtd->writesize || offs % mtd->writesize)
+ return -EINVAL;
+
+ total_written = len;
+ while (total_written) {
+ size_t to_write = mtd->erasesize - offs;
+
+ if (to_write > total_written)
+ to_write = total_written;
+
+ err = ubi_eba_write_leb(ubi, vol->vol_id, lnum, buf, offs,
+ to_write, UBI_UNKNOWN);
+ if (err)
+ break;
+
+ lnum += 1;
+ offs = 0;
+ total_written -= to_write;
+ buf += to_write;
+ }
+
+ *retlen = len - total_written;
+ return err;
+}
+
+/**
+ * gluebi_erase - erase operation of emulated MTD devices.
+ * @mtd: the MTD device description object
+ * @instr: the erase operation description
+ *
+ * This function calls the erase callback when finishes. Returns zero in case
+ * of success and a negative error code in case of failure.
+ */
+static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ int err, i, lnum, count;
+ struct ubi_volume *vol;
+ struct ubi_device *ubi;
+
+ dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr);
+
+ if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize)
+ return -EINVAL;
+
+ if (instr->len < 0 || instr->addr + instr->len > mtd->size)
+ return -EINVAL;
+
+ if (instr->addr % mtd->writesize || instr->len % mtd->writesize)
+ return -EINVAL;
+
+ lnum = instr->addr / mtd->erasesize;
+ count = instr->len / mtd->erasesize;
+
+ vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
+ ubi = vol->ubi;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ for (i = 0; i < count; i++) {
+ err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum + i);
+ if (err)
+ goto out_err;
+ }
+
+ /*
+ * MTD erase operations are synchronous, so we have to make sure the
+ * physical eraseblock is wiped out.
+ */
+ err = ubi_wl_flush(ubi);
+ if (err)
+ goto out_err;
+
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+ return 0;
+
+out_err:
+ instr->state = MTD_ERASE_FAILED;
+ instr->fail_addr = lnum * mtd->erasesize;
+ return err;
+}
+
+/**
+ * ubi_create_gluebi - initialize gluebi for an UBI volume.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ *
+ * This function is called when an UBI volume is created in order to create
+ * corresponding fake MTD device. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol)
+{
+ struct mtd_info *mtd = &vol->gluebi_mtd;
+
+ mtd->name = kmemdup(vol->name, vol->name_len + 1, GFP_KERNEL);
+ if (!mtd->name)
+ return -ENOMEM;
+
+ mtd->type = MTD_UBIVOLUME;
+ if (!ubi->ro_mode)
+ mtd->flags = MTD_WRITEABLE;
+ mtd->writesize = ubi->min_io_size;
+ mtd->owner = THIS_MODULE;
+ mtd->size = vol->usable_leb_size * vol->reserved_pebs;
+ mtd->erasesize = vol->usable_leb_size;
+ mtd->read = gluebi_read;
+ mtd->write = gluebi_write;
+ mtd->erase = gluebi_erase;
+ mtd->get_device = gluebi_get_device;
+ mtd->put_device = gluebi_put_device;
+
+ if (add_mtd_device(mtd)) {
+ ubi_err("cannot not add MTD device\n");
+ kfree(mtd->name);
+ return -ENFILE;
+ }
+
+ dbg_msg("added mtd%d (\"%s\"), size %u, EB size %u",
+ mtd->index, mtd->name, mtd->size, mtd->erasesize);
+ return 0;
+}
+
+/**
+ * ubi_destroy_gluebi - close gluebi for an UBI volume.
+ * @vol: volume description object
+ *
+ * This function is called when an UBI volume is removed in order to remove
+ * corresponding fake MTD device. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+int ubi_destroy_gluebi(struct ubi_volume *vol)
+{
+ int err;
+ struct mtd_info *mtd = &vol->gluebi_mtd;
+
+ dbg_msg("remove mtd%d", mtd->index);
+ err = del_mtd_device(mtd);
+ if (err)
+ return err;
+ kfree(mtd->name);
+ return 0;
+}
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
new file mode 100644
index 000000000000..438914d05151
--- /dev/null
+++ b/drivers/mtd/ubi/io.c
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * UBI input/output unit.
+ *
+ * This unit provides a uniform way to work with all kinds of the underlying
+ * MTD devices. It also implements handy functions for reading and writing UBI
+ * headers.
+ *
+ * We are trying to have a paranoid mindset and not to trust to what we read
+ * from the flash media in order to be more secure and robust. So this unit
+ * validates every single header it reads from the flash media.
+ *
+ * Some words about how the eraseblock headers are stored.
+ *
+ * The erase counter header is always stored at offset zero. By default, the
+ * VID header is stored after the EC header at the closest aligned offset
+ * (i.e. aligned to the minimum I/O unit size). Data starts next to the VID
+ * header at the closest aligned offset. But this default layout may be
+ * changed. For example, for different reasons (e.g., optimization) UBI may be
+ * asked to put the VID header at further offset, and even at an unaligned
+ * offset. Of course, if the offset of the VID header is unaligned, UBI adds
+ * proper padding in front of it. Data offset may also be changed but it has to
+ * be aligned.
+ *
+ * About minimal I/O units. In general, UBI assumes flash device model where
+ * there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1,
+ * in case of NAND flash it is a NAND page, etc. This is reported by MTD in the
+ * @ubi->mtd->writesize field. But as an exception, UBI admits of using another
+ * (smaller) minimal I/O unit size for EC and VID headers to make it possible
+ * to do different optimizations.
+ *
+ * This is extremely useful in case of NAND flashes which admit of several
+ * write operations to one NAND page. In this case UBI can fit EC and VID
+ * headers at one NAND page. Thus, UBI may use "sub-page" size as the minimal
+ * I/O unit for the headers (the @ubi->hdrs_min_io_size field). But it still
+ * reports NAND page size (@ubi->min_io_size) as a minimal I/O unit for the UBI
+ * users.
+ *
+ * Example: some Samsung NANDs with 2KiB pages allow 4x 512-byte writes, so
+ * although the minimal I/O unit is 2K, UBI uses 512 bytes for EC and VID
+ * headers.
+ *
+ * Q: why not just to treat sub-page as a minimal I/O unit of this flash
+ * device, e.g., make @ubi->min_io_size = 512 in the example above?
+ *
+ * A: because when writing a sub-page, MTD still writes a full 2K page but the
+ * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing
+ * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we
+ * prefer to use sub-pages only for EV and VID headers.
+ *
+ * As it was noted above, the VID header may start at a non-aligned offset.
+ * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page,
+ * the VID header may reside at offset 1984 which is the last 64 bytes of the
+ * last sub-page (EC header is always at offset zero). This causes some
+ * difficulties when reading and writing VID headers.
+ *
+ * Suppose we have a 64-byte buffer and we read a VID header at it. We change
+ * the data and want to write this VID header out. As we can only write in
+ * 512-byte chunks, we have to allocate one more buffer and copy our VID header
+ * to offset 448 of this buffer.
+ *
+ * The I/O unit does the following trick in order to avoid this extra copy.
+ * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header
+ * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the
+ * VID header is being written out, it shifts the VID header pointer back and
+ * writes the whole sub-page.
+ */
+
+#include <linux/crc32.h>
+#include <linux/err.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
+ const struct ubi_ec_hdr *ec_hdr);
+static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum);
+static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
+ const struct ubi_vid_hdr *vid_hdr);
+static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum,
+ int offset, int len);
+#else
+#define paranoid_check_not_bad(ubi, pnum) 0
+#define paranoid_check_peb_ec_hdr(ubi, pnum) 0
+#define paranoid_check_ec_hdr(ubi, pnum, ec_hdr) 0
+#define paranoid_check_peb_vid_hdr(ubi, pnum) 0
+#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0
+#define paranoid_check_all_ff(ubi, pnum, offset, len) 0
+#endif
+
+/**
+ * ubi_io_read - read data from a physical eraseblock.
+ * @ubi: UBI device description object
+ * @buf: buffer where to store the read data
+ * @pnum: physical eraseblock number to read from
+ * @offset: offset within the physical eraseblock from where to read
+ * @len: how many bytes to read
+ *
+ * This function reads data from offset @offset of physical eraseblock @pnum
+ * and stores the read data in the @buf buffer. The following return codes are
+ * possible:
+ *
+ * o %0 if all the requested data were successfully read;
+ * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but
+ * correctable bit-flips were detected; this is harmless but may indicate
+ * that this eraseblock may become bad soon (but do not have to);
+ * o %-EBADMSG if the MTD subsystem reported about data data integrity
+ * problems, for example it can me an ECC error in case of NAND; this most
+ * probably means that the data is corrupted;
+ * o %-EIO if some I/O error occurred;
+ * o other negative error codes in case of other errors.
+ */
+int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
+ int len)
+{
+ int err, retries = 0;
+ size_t read;
+ loff_t addr;
+
+ dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset);
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+ ubi_assert(offset >= 0 && offset + len <= ubi->peb_size);
+ ubi_assert(len > 0);
+
+ err = paranoid_check_not_bad(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+
+ addr = (loff_t)pnum * ubi->peb_size + offset;
+retry:
+ err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
+ if (err) {
+ if (err == -EUCLEAN) {
+ /*
+ * -EUCLEAN is reported if there was a bit-flip which
+ * was corrected, so this is harmless.
+ */
+ ubi_msg("fixable bit-flip detected at PEB %d", pnum);
+ ubi_assert(len == read);
+ return UBI_IO_BITFLIPS;
+ }
+
+ if (read != len && retries++ < UBI_IO_RETRIES) {
+ dbg_io("error %d while reading %d bytes from PEB %d:%d, "
+ "read only %zd bytes, retry",
+ err, len, pnum, offset, read);
+ yield();
+ goto retry;
+ }
+
+ ubi_err("error %d while reading %d bytes from PEB %d:%d, "
+ "read %zd bytes", err, len, pnum, offset, read);
+ ubi_dbg_dump_stack();
+ } else {
+ ubi_assert(len == read);
+
+ if (ubi_dbg_is_bitflip()) {
+ dbg_msg("bit-flip (emulated)");
+ err = UBI_IO_BITFLIPS;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * ubi_io_write - write data to a physical eraseblock.
+ * @ubi: UBI device description object
+ * @buf: buffer with the data to write
+ * @pnum: physical eraseblock number to write to
+ * @offset: offset within the physical eraseblock where to write
+ * @len: how many bytes to write
+ *
+ * This function writes @len bytes of data from buffer @buf to offset @offset
+ * of physical eraseblock @pnum. If all the data were successfully written,
+ * zero is returned. If an error occurred, this function returns a negative
+ * error code. If %-EIO is returned, the physical eraseblock most probably went
+ * bad.
+ *
+ * Note, in case of an error, it is possible that something was still written
+ * to the flash media, but may be some garbage.
+ */
+int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum,
+ int offset, int len)
+{
+ int err;
+ size_t written;
+ loff_t addr;
+
+ dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset);
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+ ubi_assert(offset >= 0 && offset + len <= ubi->peb_size);
+ ubi_assert(offset % ubi->hdrs_min_io_size == 0);
+ ubi_assert(len > 0 && len % ubi->hdrs_min_io_size == 0);
+
+ if (ubi->ro_mode) {
+ ubi_err("read-only mode");
+ return -EROFS;
+ }
+
+ /* The below has to be compiled out if paranoid checks are disabled */
+
+ err = paranoid_check_not_bad(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+
+ /* The area we are writing to has to contain all 0xFF bytes */
+ err = paranoid_check_all_ff(ubi, pnum, offset, len);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+
+ if (offset >= ubi->leb_start) {
+ /*
+ * We write to the data area of the physical eraseblock. Make
+ * sure it has valid EC and VID headers.
+ */
+ err = paranoid_check_peb_ec_hdr(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+ err = paranoid_check_peb_vid_hdr(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+ }
+
+ if (ubi_dbg_is_write_failure()) {
+ dbg_err("cannot write %d bytes to PEB %d:%d "
+ "(emulated)", len, pnum, offset);
+ ubi_dbg_dump_stack();
+ return -EIO;
+ }
+
+ addr = (loff_t)pnum * ubi->peb_size + offset;
+ err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf);
+ if (err) {
+ ubi_err("error %d while writing %d bytes to PEB %d:%d, written"
+ " %zd bytes", err, len, pnum, offset, written);
+ ubi_dbg_dump_stack();
+ } else
+ ubi_assert(written == len);
+
+ return err;
+}
+
+/**
+ * erase_callback - MTD erasure call-back.
+ * @ei: MTD erase information object.
+ *
+ * Note, even though MTD erase interface is asynchronous, all the current
+ * implementations are synchronous anyway.
+ */
+static void erase_callback(struct erase_info *ei)
+{
+ wake_up_interruptible((wait_queue_head_t *)ei->priv);
+}
+
+/**
+ * do_sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to erase
+ *
+ * This function synchronously erases physical eraseblock @pnum and returns
+ * zero in case of success and a negative error code in case of failure. If
+ * %-EIO is returned, the physical eraseblock most probably went bad.
+ */
+static int do_sync_erase(const struct ubi_device *ubi, int pnum)
+{
+ int err, retries = 0;
+ struct erase_info ei;
+ wait_queue_head_t wq;
+
+ dbg_io("erase PEB %d", pnum);
+
+retry:
+ init_waitqueue_head(&wq);
+ memset(&ei, 0, sizeof(struct erase_info));
+
+ ei.mtd = ubi->mtd;
+ ei.addr = pnum * ubi->peb_size;
+ ei.len = ubi->peb_size;
+ ei.callback = erase_callback;
+ ei.priv = (unsigned long)&wq;
+
+ err = ubi->mtd->erase(ubi->mtd, &ei);
+ if (err) {
+ if (retries++ < UBI_IO_RETRIES) {
+ dbg_io("error %d while erasing PEB %d, retry",
+ err, pnum);
+ yield();
+ goto retry;
+ }
+ ubi_err("cannot erase PEB %d, error %d", pnum, err);
+ ubi_dbg_dump_stack();
+ return err;
+ }
+
+ err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE ||
+ ei.state == MTD_ERASE_FAILED);
+ if (err) {
+ ubi_err("interrupted PEB %d erasure", pnum);
+ return -EINTR;
+ }
+
+ if (ei.state == MTD_ERASE_FAILED) {
+ if (retries++ < UBI_IO_RETRIES) {
+ dbg_io("error while erasing PEB %d, retry", pnum);
+ yield();
+ goto retry;
+ }
+ ubi_err("cannot erase PEB %d", pnum);
+ ubi_dbg_dump_stack();
+ return -EIO;
+ }
+
+ err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size);
+ if (err)
+ return err > 0 ? -EINVAL : err;
+
+ if (ubi_dbg_is_erase_failure() && !err) {
+ dbg_err("cannot erase PEB %d (emulated)", pnum);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * check_pattern - check if buffer contains only a certain byte pattern.
+ * @buf: buffer to check
+ * @patt: the pattern to check
+ * @size: buffer size in bytes
+ *
+ * This function returns %1 in there are only @patt bytes in @buf, and %0 if
+ * something else was also found.
+ */
+static int check_pattern(const void *buf, uint8_t patt, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (((const uint8_t *)buf)[i] != patt)
+ return 0;
+ return 1;
+}
+
+/* Patterns to write to a physical eraseblock when torturing it */
+static uint8_t patterns[] = {0xa5, 0x5a, 0x0};
+
+/**
+ * torture_peb - test a supposedly bad physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to test
+ *
+ * This function returns %-EIO if the physical eraseblock did not pass the
+ * test, a positive number of erase operations done if the test was
+ * successfully passed, and other negative error codes in case of other errors.
+ */
+static int torture_peb(const struct ubi_device *ubi, int pnum)
+{
+ void *buf;
+ int err, i, patt_count;
+
+ buf = kmalloc(ubi->peb_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ patt_count = ARRAY_SIZE(patterns);
+ ubi_assert(patt_count > 0);
+
+ for (i = 0; i < patt_count; i++) {
+ err = do_sync_erase(ubi, pnum);
+ if (err)
+ goto out;
+
+ /* Make sure the PEB contains only 0xFF bytes */
+ err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size);
+ if (err)
+ goto out;
+
+ err = check_pattern(buf, 0xFF, ubi->peb_size);
+ if (err == 0) {
+ ubi_err("erased PEB %d, but a non-0xFF byte found",
+ pnum);
+ err = -EIO;
+ goto out;
+ }
+
+ /* Write a pattern and check it */
+ memset(buf, patterns[i], ubi->peb_size);
+ err = ubi_io_write(ubi, buf, pnum, 0, ubi->peb_size);
+ if (err)
+ goto out;
+
+ memset(buf, ~patterns[i], ubi->peb_size);
+ err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size);
+ if (err)
+ goto out;
+
+ err = check_pattern(buf, patterns[i], ubi->peb_size);
+ if (err == 0) {
+ ubi_err("pattern %x checking failed for PEB %d",
+ patterns[i], pnum);
+ err = -EIO;
+ goto out;
+ }
+ }
+
+ err = patt_count;
+
+out:
+ if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
+ /*
+ * If a bit-flip or data integrity error was detected, the test
+ * has not passed because it happened on a freshly erased
+ * physical eraseblock which means something is wrong with it.
+ */
+ err = -EIO;
+ kfree(buf);
+ return err;
+}
+
+/**
+ * ubi_io_sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to erase
+ * @torture: if this physical eraseblock has to be tortured
+ *
+ * This function synchronously erases physical eraseblock @pnum. If @torture
+ * flag is not zero, the physical eraseblock is checked by means of writing
+ * different patterns to it and reading them back. If the torturing is enabled,
+ * the physical eraseblock is erased more then once.
+ *
+ * This function returns the number of erasures made in case of success, %-EIO
+ * if the erasure failed or the torturing test failed, and other negative error
+ * codes in case of other errors. Note, %-EIO means that the physical
+ * eraseblock is bad.
+ */
+int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture)
+{
+ int err, ret = 0;
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ err = paranoid_check_not_bad(ubi, pnum);
+ if (err != 0)
+ return err > 0 ? -EINVAL : err;
+
+ if (ubi->ro_mode) {
+ ubi_err("read-only mode");
+ return -EROFS;
+ }
+
+ if (torture) {
+ ret = torture_peb(ubi, pnum);
+ if (ret < 0)
+ return ret;
+ }
+
+ err = do_sync_erase(ubi, pnum);
+ if (err)
+ return err;
+
+ return ret + 1;
+}
+
+/**
+ * ubi_io_is_bad - check if a physical eraseblock is bad.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns a positive number if the physical eraseblock is bad,
+ * zero if not, and a negative error code if an error occurred.
+ */
+int ubi_io_is_bad(const struct ubi_device *ubi, int pnum)
+{
+ struct mtd_info *mtd = ubi->mtd;
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ if (ubi->bad_allowed) {
+ int ret;
+
+ ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size);
+ if (ret < 0)
+ ubi_err("error %d while checking if PEB %d is bad",
+ ret, pnum);
+ else if (ret)
+ dbg_io("PEB %d is bad", pnum);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ubi_io_mark_bad - mark a physical eraseblock as bad.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to mark
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum)
+{
+ int err;
+ struct mtd_info *mtd = ubi->mtd;
+
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ if (ubi->ro_mode) {
+ ubi_err("read-only mode");
+ return -EROFS;
+ }
+
+ if (!ubi->bad_allowed)
+ return 0;
+
+ err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size);
+ if (err)
+ ubi_err("cannot mark PEB %d bad, error %d", pnum, err);
+ return err;
+}
+
+/**
+ * validate_ec_hdr - validate an erase counter header.
+ * @ubi: UBI device description object
+ * @ec_hdr: the erase counter header to check
+ *
+ * This function returns zero if the erase counter header is OK, and %1 if
+ * not.
+ */
+static int validate_ec_hdr(const struct ubi_device *ubi,
+ const struct ubi_ec_hdr *ec_hdr)
+{
+ long long ec;
+ int vid_hdr_offset, leb_start;
+
+ ec = ubi64_to_cpu(ec_hdr->ec);
+ vid_hdr_offset = ubi32_to_cpu(ec_hdr->vid_hdr_offset);
+ leb_start = ubi32_to_cpu(ec_hdr->data_offset);
+
+ if (ec_hdr->version != UBI_VERSION) {
+ ubi_err("node with incompatible UBI version found: "
+ "this UBI version is %d, image version is %d",
+ UBI_VERSION, (int)ec_hdr->version);
+ goto bad;
+ }
+
+ if (vid_hdr_offset != ubi->vid_hdr_offset) {
+ ubi_err("bad VID header offset %d, expected %d",
+ vid_hdr_offset, ubi->vid_hdr_offset);
+ goto bad;
+ }
+
+ if (leb_start != ubi->leb_start) {
+ ubi_err("bad data offset %d, expected %d",
+ leb_start, ubi->leb_start);
+ goto bad;
+ }
+
+ if (ec < 0 || ec > UBI_MAX_ERASECOUNTER) {
+ ubi_err("bad erase counter %lld", ec);
+ goto bad;
+ }
+
+ return 0;
+
+bad:
+ ubi_err("bad EC header");
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+/**
+ * ubi_io_read_ec_hdr - read and check an erase counter header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to read from
+ * @ec_hdr: a &struct ubi_ec_hdr object where to store the read erase counter
+ * header
+ * @verbose: be verbose if the header is corrupted or was not found
+ *
+ * This function reads erase counter header from physical eraseblock @pnum and
+ * stores it in @ec_hdr. This function also checks CRC checksum of the read
+ * erase counter header. The following codes may be returned:
+ *
+ * o %0 if the CRC checksum is correct and the header was successfully read;
+ * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected
+ * and corrected by the flash driver; this is harmless but may indicate that
+ * this eraseblock may become bad soon (but may be not);
+ * o %UBI_IO_BAD_EC_HDR if the erase counter header is corrupted (a CRC error);
+ * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty;
+ * o a negative error code in case of failure.
+ */
+int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum,
+ struct ubi_ec_hdr *ec_hdr, int verbose)
+{
+ int err, read_err = 0;
+ uint32_t crc, magic, hdr_crc;
+
+ dbg_io("read EC header from PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE);
+ if (err) {
+ if (err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ return err;
+
+ /*
+ * We read all the data, but either a correctable bit-flip
+ * occurred, or MTD reported about some data integrity error,
+ * like an ECC error in case of NAND. The former is harmless,
+ * the later may mean that the read data is corrupted. But we
+ * have a CRC check-sum and we will detect this. If the EC
+ * header is still OK, we just report this as there was a
+ * bit-flip.
+ */
+ read_err = err;
+ }
+
+ magic = ubi32_to_cpu(ec_hdr->magic);
+ if (magic != UBI_EC_HDR_MAGIC) {
+ /*
+ * The magic field is wrong. Let's check if we have read all
+ * 0xFF. If yes, this physical eraseblock is assumed to be
+ * empty.
+ *
+ * But if there was a read error, we do not test it for all
+ * 0xFFs. Even if it does contain all 0xFFs, this error
+ * indicates that something is still wrong with this physical
+ * eraseblock and we anyway cannot treat it as empty.
+ */
+ if (read_err != -EBADMSG &&
+ check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) {
+ /* The physical eraseblock is supposedly empty */
+
+ /*
+ * The below is just a paranoid check, it has to be
+ * compiled out if paranoid checks are disabled.
+ */
+ err = paranoid_check_all_ff(ubi, pnum, 0,
+ ubi->peb_size);
+ if (err)
+ return err > 0 ? UBI_IO_BAD_EC_HDR : err;
+
+ if (verbose)
+ ubi_warn("no EC header found at PEB %d, "
+ "only 0xFF bytes", pnum);
+ return UBI_IO_PEB_EMPTY;
+ }
+
+ /*
+ * This is not a valid erase counter header, and these are not
+ * 0xFF bytes. Report that the header is corrupted.
+ */
+ if (verbose) {
+ ubi_warn("bad magic number at PEB %d: %08x instead of "
+ "%08x", pnum, magic, UBI_EC_HDR_MAGIC);
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ }
+ return UBI_IO_BAD_EC_HDR;
+ }
+
+ crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+ hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc);
+
+ if (hdr_crc != crc) {
+ if (verbose) {
+ ubi_warn("bad EC header CRC at PEB %d, calculated %#08x,"
+ " read %#08x", pnum, crc, hdr_crc);
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ }
+ return UBI_IO_BAD_EC_HDR;
+ }
+
+ /* And of course validate what has just been read from the media */
+ err = validate_ec_hdr(ubi, ec_hdr);
+ if (err) {
+ ubi_err("validation failed for PEB %d", pnum);
+ return -EINVAL;
+ }
+
+ return read_err ? UBI_IO_BITFLIPS : 0;
+}
+
+/**
+ * ubi_io_write_ec_hdr - write an erase counter header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to write to
+ * @ec_hdr: the erase counter header to write
+ *
+ * This function writes erase counter header described by @ec_hdr to physical
+ * eraseblock @pnum. It also fills most fields of @ec_hdr before writing, so
+ * the caller do not have to fill them. Callers must only fill the @ec_hdr->ec
+ * field.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If %-EIO is returned, the physical eraseblock most probably
+ * went bad.
+ */
+int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum,
+ struct ubi_ec_hdr *ec_hdr)
+{
+ int err;
+ uint32_t crc;
+
+ dbg_io("write EC header to PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ ec_hdr->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC);
+ ec_hdr->version = UBI_VERSION;
+ ec_hdr->vid_hdr_offset = cpu_to_ubi32(ubi->vid_hdr_offset);
+ ec_hdr->data_offset = cpu_to_ubi32(ubi->leb_start);
+ crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+ ec_hdr->hdr_crc = cpu_to_ubi32(crc);
+
+ err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr);
+ if (err)
+ return -EINVAL;
+
+ err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize);
+ return err;
+}
+
+/**
+ * validate_vid_hdr - validate a volume identifier header.
+ * @ubi: UBI device description object
+ * @vid_hdr: the volume identifier header to check
+ *
+ * This function checks that data stored in the volume identifier header
+ * @vid_hdr. Returns zero if the VID header is OK and %1 if not.
+ */
+static int validate_vid_hdr(const struct ubi_device *ubi,
+ const struct ubi_vid_hdr *vid_hdr)
+{
+ int vol_type = vid_hdr->vol_type;
+ int copy_flag = vid_hdr->copy_flag;
+ int vol_id = ubi32_to_cpu(vid_hdr->vol_id);
+ int lnum = ubi32_to_cpu(vid_hdr->lnum);
+ int compat = vid_hdr->compat;
+ int data_size = ubi32_to_cpu(vid_hdr->data_size);
+ int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs);
+ int data_pad = ubi32_to_cpu(vid_hdr->data_pad);
+ int data_crc = ubi32_to_cpu(vid_hdr->data_crc);
+ int usable_leb_size = ubi->leb_size - data_pad;
+
+ if (copy_flag != 0 && copy_flag != 1) {
+ dbg_err("bad copy_flag");
+ goto bad;
+ }
+
+ if (vol_id < 0 || lnum < 0 || data_size < 0 || used_ebs < 0 ||
+ data_pad < 0) {
+ dbg_err("negative values");
+ goto bad;
+ }
+
+ if (vol_id >= UBI_MAX_VOLUMES && vol_id < UBI_INTERNAL_VOL_START) {
+ dbg_err("bad vol_id");
+ goto bad;
+ }
+
+ if (vol_id < UBI_INTERNAL_VOL_START && compat != 0) {
+ dbg_err("bad compat");
+ goto bad;
+ }
+
+ if (vol_id >= UBI_INTERNAL_VOL_START && compat != UBI_COMPAT_DELETE &&
+ compat != UBI_COMPAT_RO && compat != UBI_COMPAT_PRESERVE &&
+ compat != UBI_COMPAT_REJECT) {
+ dbg_err("bad compat");
+ goto bad;
+ }
+
+ if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) {
+ dbg_err("bad vol_type");
+ goto bad;
+ }
+
+ if (data_pad >= ubi->leb_size / 2) {
+ dbg_err("bad data_pad");
+ goto bad;
+ }
+
+ if (vol_type == UBI_VID_STATIC) {
+ /*
+ * Although from high-level point of view static volumes may
+ * contain zero bytes of data, but no VID headers can contain
+ * zero at these fields, because they empty volumes do not have
+ * mapped logical eraseblocks.
+ */
+ if (used_ebs == 0) {
+ dbg_err("zero used_ebs");
+ goto bad;
+ }
+ if (data_size == 0) {
+ dbg_err("zero data_size");
+ goto bad;
+ }
+ if (lnum < used_ebs - 1) {
+ if (data_size != usable_leb_size) {
+ dbg_err("bad data_size");
+ goto bad;
+ }
+ } else if (lnum == used_ebs - 1) {
+ if (data_size == 0) {
+ dbg_err("bad data_size at last LEB");
+ goto bad;
+ }
+ } else {
+ dbg_err("too high lnum");
+ goto bad;
+ }
+ } else {
+ if (copy_flag == 0) {
+ if (data_crc != 0) {
+ dbg_err("non-zero data CRC");
+ goto bad;
+ }
+ if (data_size != 0) {
+ dbg_err("non-zero data_size");
+ goto bad;
+ }
+ } else {
+ if (data_size == 0) {
+ dbg_err("zero data_size of copy");
+ goto bad;
+ }
+ }
+ if (used_ebs != 0) {
+ dbg_err("bad used_ebs");
+ goto bad;
+ }
+ }
+
+ return 0;
+
+bad:
+ ubi_err("bad VID header");
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+/**
+ * ubi_io_read_vid_hdr - read and check a volume identifier header.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to read from
+ * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume
+ * identifier header
+ * @verbose: be verbose if the header is corrupted or wasn't found
+ *
+ * This function reads the volume identifier header from physical eraseblock
+ * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read
+ * volume identifier header. The following codes may be returned:
+ *
+ * o %0 if the CRC checksum is correct and the header was successfully read;
+ * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected
+ * and corrected by the flash driver; this is harmless but may indicate that
+ * this eraseblock may become bad soon;
+ * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC
+ * error detected);
+ * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID
+ * header there);
+ * o a negative error code in case of failure.
+ */
+int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum,
+ struct ubi_vid_hdr *vid_hdr, int verbose)
+{
+ int err, read_err = 0;
+ uint32_t crc, magic, hdr_crc;
+ void *p;
+
+ dbg_io("read VID header from PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ p = (char *)vid_hdr - ubi->vid_hdr_shift;
+ err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
+ ubi->vid_hdr_alsize);
+ if (err) {
+ if (err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ return err;
+
+ /*
+ * We read all the data, but either a correctable bit-flip
+ * occurred, or MTD reported about some data integrity error,
+ * like an ECC error in case of NAND. The former is harmless,
+ * the later may mean the read data is corrupted. But we have a
+ * CRC check-sum and we will identify this. If the VID header is
+ * still OK, we just report this as there was a bit-flip.
+ */
+ read_err = err;
+ }
+
+ magic = ubi32_to_cpu(vid_hdr->magic);
+ if (magic != UBI_VID_HDR_MAGIC) {
+ /*
+ * If we have read all 0xFF bytes, the VID header probably does
+ * not exist and the physical eraseblock is assumed to be free.
+ *
+ * But if there was a read error, we do not test the data for
+ * 0xFFs. Even if it does contain all 0xFFs, this error
+ * indicates that something is still wrong with this physical
+ * eraseblock and it cannot be regarded as free.
+ */
+ if (read_err != -EBADMSG &&
+ check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) {
+ /* The physical eraseblock is supposedly free */
+
+ /*
+ * The below is just a paranoid check, it has to be
+ * compiled out if paranoid checks are disabled.
+ */
+ err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start,
+ ubi->leb_size);
+ if (err)
+ return err > 0 ? UBI_IO_BAD_VID_HDR : err;
+
+ if (verbose)
+ ubi_warn("no VID header found at PEB %d, "
+ "only 0xFF bytes", pnum);
+ return UBI_IO_PEB_FREE;
+ }
+
+ /*
+ * This is not a valid VID header, and these are not 0xFF
+ * bytes. Report that the header is corrupted.
+ */
+ if (verbose) {
+ ubi_warn("bad magic number at PEB %d: %08x instead of "
+ "%08x", pnum, magic, UBI_VID_HDR_MAGIC);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ }
+ return UBI_IO_BAD_VID_HDR;
+ }
+
+ crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC);
+ hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc);
+
+ if (hdr_crc != crc) {
+ if (verbose) {
+ ubi_warn("bad CRC at PEB %d, calculated %#08x, "
+ "read %#08x", pnum, crc, hdr_crc);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ }
+ return UBI_IO_BAD_VID_HDR;
+ }
+
+ /* Validate the VID header that we have just read */
+ err = validate_vid_hdr(ubi, vid_hdr);
+ if (err) {
+ ubi_err("validation failed for PEB %d", pnum);
+ return -EINVAL;
+ }
+
+ return read_err ? UBI_IO_BITFLIPS : 0;
+}
+
+/**
+ * ubi_io_write_vid_hdr - write a volume identifier header.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to write to
+ * @vid_hdr: the volume identifier header to write
+ *
+ * This function writes the volume identifier header described by @vid_hdr to
+ * physical eraseblock @pnum. This function automatically fills the
+ * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates
+ * header CRC checksum and stores it at vid_hdr->hdr_crc.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If %-EIO is returned, the physical eraseblock probably went
+ * bad.
+ */
+int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum,
+ struct ubi_vid_hdr *vid_hdr)
+{
+ int err;
+ uint32_t crc;
+ void *p;
+
+ dbg_io("write VID header to PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ err = paranoid_check_peb_ec_hdr(ubi, pnum);
+ if (err)
+ return err > 0 ? -EINVAL: err;
+
+ vid_hdr->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC);
+ vid_hdr->version = UBI_VERSION;
+ crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC);
+ vid_hdr->hdr_crc = cpu_to_ubi32(crc);
+
+ err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr);
+ if (err)
+ return -EINVAL;
+
+ p = (char *)vid_hdr - ubi->vid_hdr_shift;
+ err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
+ ubi->vid_hdr_alsize);
+ return err;
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_not_bad - ensure that a physical eraseblock is not bad.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number to check
+ *
+ * This function returns zero if the physical eraseblock is good, a positive
+ * number if it is bad and a negative error code if an error occurred.
+ */
+static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
+{
+ int err;
+
+ err = ubi_io_is_bad(ubi, pnum);
+ if (!err)
+ return err;
+
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_dbg_dump_stack();
+ return err;
+}
+
+/**
+ * paranoid_check_ec_hdr - check if an erase counter header is all right.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number the erase counter header belongs to
+ * @ec_hdr: the erase counter header to check
+ *
+ * This function returns zero if the erase counter header contains valid
+ * values, and %1 if not.
+ */
+static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
+ const struct ubi_ec_hdr *ec_hdr)
+{
+ int err;
+ uint32_t magic;
+
+ magic = ubi32_to_cpu(ec_hdr->magic);
+ if (magic != UBI_EC_HDR_MAGIC) {
+ ubi_err("bad magic %#08x, must be %#08x",
+ magic, UBI_EC_HDR_MAGIC);
+ goto fail;
+ }
+
+ err = validate_ec_hdr(ubi, ec_hdr);
+ if (err) {
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+/**
+ * paranoid_check_peb_ec_hdr - check that the erase counter header of a
+ * physical eraseblock is in-place and is all right.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns zero if the erase counter header is all right, %1 if
+ * not, and a negative error code if an error occurred.
+ */
+static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
+{
+ int err;
+ uint32_t crc, hdr_crc;
+ struct ubi_ec_hdr *ec_hdr;
+
+ ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!ec_hdr)
+ return -ENOMEM;
+
+ err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE);
+ if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ goto exit;
+
+ crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC);
+ hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc);
+ if (hdr_crc != crc) {
+ ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc);
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_dbg_dump_ec_hdr(ec_hdr);
+ ubi_dbg_dump_stack();
+ err = 1;
+ goto exit;
+ }
+
+ err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr);
+
+exit:
+ kfree(ec_hdr);
+ return err;
+}
+
+/**
+ * paranoid_check_vid_hdr - check that a volume identifier header is all right.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock number the volume identifier header belongs to
+ * @vid_hdr: the volume identifier header to check
+ *
+ * This function returns zero if the volume identifier header is all right, and
+ * %1 if not.
+ */
+static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
+ const struct ubi_vid_hdr *vid_hdr)
+{
+ int err;
+ uint32_t magic;
+
+ magic = ubi32_to_cpu(vid_hdr->magic);
+ if (magic != UBI_VID_HDR_MAGIC) {
+ ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x",
+ magic, pnum, UBI_VID_HDR_MAGIC);
+ goto fail;
+ }
+
+ err = validate_vid_hdr(ubi, vid_hdr);
+ if (err) {
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ goto fail;
+ }
+
+ return err;
+
+fail:
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ ubi_dbg_dump_stack();
+ return 1;
+
+}
+
+/**
+ * paranoid_check_peb_vid_hdr - check that the volume identifier header of a
+ * physical eraseblock is in-place and is all right.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ *
+ * This function returns zero if the volume identifier header is all right,
+ * %1 if not, and a negative error code if an error occurred.
+ */
+static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
+{
+ int err;
+ uint32_t crc, hdr_crc;
+ struct ubi_vid_hdr *vid_hdr;
+ void *p;
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ p = (char *)vid_hdr - ubi->vid_hdr_shift;
+ err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
+ ubi->vid_hdr_alsize);
+ if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG)
+ goto exit;
+
+ crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC);
+ hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc);
+ if (hdr_crc != crc) {
+ ubi_err("bad VID header CRC at PEB %d, calculated %#08x, "
+ "read %#08x", pnum, crc, hdr_crc);
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ ubi_dbg_dump_stack();
+ err = 1;
+ goto exit;
+ }
+
+ err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr);
+
+exit:
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+}
+
+/**
+ * paranoid_check_all_ff - check that a region of flash is empty.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ * @offset: the starting offset within the physical eraseblock to check
+ * @len: the length of the region to check
+ *
+ * This function returns zero if only 0xFF bytes are present at offset
+ * @offset of the physical eraseblock @pnum, %1 if not, and a negative error
+ * code if an error occurred.
+ */
+static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum,
+ int offset, int len)
+{
+ size_t read;
+ int err;
+ void *buf;
+ loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
+
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
+ if (err && err != -EUCLEAN) {
+ ubi_err("error %d while reading %d bytes from PEB %d:%d, "
+ "read %zd bytes", err, len, pnum, offset, read);
+ goto error;
+ }
+
+ err = check_pattern(buf, 0xFF, len);
+ if (err == 0) {
+ ubi_err("flash region at PEB %d:%d, length %d does not "
+ "contain all 0xFF bytes", pnum, offset, len);
+ goto fail;
+ }
+
+ kfree(buf);
+ return 0;
+
+fail:
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ dbg_msg("hex dump of the %d-%d region", offset, offset + len);
+ ubi_dbg_hexdump(buf, len);
+ err = 1;
+error:
+ ubi_dbg_dump_stack();
+ kfree(buf);
+ return err;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
new file mode 100644
index 000000000000..d352c4575c3d
--- /dev/null
+++ b/drivers/mtd/ubi/kapi.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/* This file mostly implements UBI kernel API functions */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+/**
+ * ubi_get_device_info - get information about UBI device.
+ * @ubi_num: UBI device number
+ * @di: the information is stored here
+ *
+ * This function returns %0 in case of success and a %-ENODEV if there is no
+ * such UBI device.
+ */
+int ubi_get_device_info(int ubi_num, struct ubi_device_info *di)
+{
+ const struct ubi_device *ubi;
+
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES ||
+ !ubi_devices[ubi_num]) {
+ module_put(THIS_MODULE);
+ return -ENODEV;
+ }
+
+ ubi = ubi_devices[ubi_num];
+ di->ubi_num = ubi->ubi_num;
+ di->leb_size = ubi->leb_size;
+ di->min_io_size = ubi->min_io_size;
+ di->ro_mode = ubi->ro_mode;
+ di->cdev = MKDEV(ubi->major, 0);
+ module_put(THIS_MODULE);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ubi_get_device_info);
+
+/**
+ * ubi_get_volume_info - get information about UBI volume.
+ * @desc: volume descriptor
+ * @vi: the information is stored here
+ */
+void ubi_get_volume_info(struct ubi_volume_desc *desc,
+ struct ubi_volume_info *vi)
+{
+ const struct ubi_volume *vol = desc->vol;
+ const struct ubi_device *ubi = vol->ubi;
+
+ vi->vol_id = vol->vol_id;
+ vi->ubi_num = ubi->ubi_num;
+ vi->size = vol->reserved_pebs;
+ vi->used_bytes = vol->used_bytes;
+ vi->vol_type = vol->vol_type;
+ vi->corrupted = vol->corrupted;
+ vi->upd_marker = vol->upd_marker;
+ vi->alignment = vol->alignment;
+ vi->usable_leb_size = vol->usable_leb_size;
+ vi->name_len = vol->name_len;
+ vi->name = vol->name;
+ vi->cdev = MKDEV(ubi->major, vi->vol_id + 1);
+}
+EXPORT_SYMBOL_GPL(ubi_get_volume_info);
+
+/**
+ * ubi_open_volume - open UBI volume.
+ * @ubi_num: UBI device number
+ * @vol_id: volume ID
+ * @mode: open mode
+ *
+ * The @mode parameter specifies if the volume should be opened in read-only
+ * mode, read-write mode, or exclusive mode. The exclusive mode guarantees that
+ * nobody else will be able to open this volume. UBI allows to have many volume
+ * readers and one writer at a time.
+ *
+ * If a static volume is being opened for the first time since boot, it will be
+ * checked by this function, which means it will be fully read and the CRC
+ * checksum of each logical eraseblock will be checked.
+ *
+ * This function returns volume descriptor in case of success and a negative
+ * error code in case of failure.
+ */
+struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
+{
+ int err;
+ struct ubi_volume_desc *desc;
+ struct ubi_device *ubi = ubi_devices[ubi_num];
+ struct ubi_volume *vol;
+
+ dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode);
+
+ err = -ENODEV;
+ if (!try_module_get(THIS_MODULE))
+ return ERR_PTR(err);
+
+ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi)
+ goto out_put;
+
+ err = -EINVAL;
+ if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
+ goto out_put;
+ if (mode != UBI_READONLY && mode != UBI_READWRITE &&
+ mode != UBI_EXCLUSIVE)
+ goto out_put;
+
+ desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL);
+ if (!desc) {
+ err = -ENOMEM;
+ goto out_put;
+ }
+
+ spin_lock(&ubi->volumes_lock);
+ vol = ubi->volumes[vol_id];
+ if (!vol) {
+ err = -ENODEV;
+ goto out_unlock;
+ }
+
+ err = -EBUSY;
+ switch (mode) {
+ case UBI_READONLY:
+ if (vol->exclusive)
+ goto out_unlock;
+ vol->readers += 1;
+ break;
+
+ case UBI_READWRITE:
+ if (vol->exclusive || vol->writers > 0)
+ goto out_unlock;
+ vol->writers += 1;
+ break;
+
+ case UBI_EXCLUSIVE:
+ if (vol->exclusive || vol->writers || vol->readers)
+ goto out_unlock;
+ vol->exclusive = 1;
+ break;
+ }
+ spin_unlock(&ubi->volumes_lock);
+
+ desc->vol = vol;
+ desc->mode = mode;
+
+ /*
+ * To prevent simultaneous checks of the same volume we use @vtbl_mutex,
+ * although it is not the purpose it was introduced for.
+ */
+ mutex_lock(&ubi->vtbl_mutex);
+ if (!vol->checked) {
+ /* This is the first open - check the volume */
+ err = ubi_check_volume(ubi, vol_id);
+ if (err < 0) {
+ mutex_unlock(&ubi->vtbl_mutex);
+ ubi_close_volume(desc);
+ return ERR_PTR(err);
+ }
+ if (err == 1) {
+ ubi_warn("volume %d on UBI device %d is corrupted",
+ vol_id, ubi->ubi_num);
+ vol->corrupted = 1;
+ }
+ vol->checked = 1;
+ }
+ mutex_unlock(&ubi->vtbl_mutex);
+ return desc;
+
+out_unlock:
+ spin_unlock(&ubi->volumes_lock);
+ kfree(desc);
+out_put:
+ module_put(THIS_MODULE);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(ubi_open_volume);
+
+/**
+ * ubi_open_volume_nm - open UBI volume by name.
+ * @ubi_num: UBI device number
+ * @name: volume name
+ * @mode: open mode
+ *
+ * This function is similar to 'ubi_open_volume()', but opens a volume by name.
+ */
+struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
+ int mode)
+{
+ int i, vol_id = -1, len;
+ struct ubi_volume_desc *ret;
+ struct ubi_device *ubi;
+
+ dbg_msg("open volume %s, mode %d", name, mode);
+
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
+ len = strnlen(name, UBI_VOL_NAME_MAX + 1);
+ if (len > UBI_VOL_NAME_MAX)
+ return ERR_PTR(-EINVAL);
+
+ ret = ERR_PTR(-ENODEV);
+ if (!try_module_get(THIS_MODULE))
+ return ret;
+
+ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi_devices[ubi_num])
+ goto out_put;
+
+ ubi = ubi_devices[ubi_num];
+
+ spin_lock(&ubi->volumes_lock);
+ /* Walk all volumes of this UBI device */
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ struct ubi_volume *vol = ubi->volumes[i];
+
+ if (vol && len == vol->name_len && !strcmp(name, vol->name)) {
+ vol_id = i;
+ break;
+ }
+ }
+ spin_unlock(&ubi->volumes_lock);
+
+ if (vol_id < 0)
+ goto out_put;
+
+ ret = ubi_open_volume(ubi_num, vol_id, mode);
+
+out_put:
+ module_put(THIS_MODULE);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
+
+/**
+ * ubi_close_volume - close UBI volume.
+ * @desc: volume descriptor
+ */
+void ubi_close_volume(struct ubi_volume_desc *desc)
+{
+ struct ubi_volume *vol = desc->vol;
+
+ dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode);
+
+ spin_lock(&vol->ubi->volumes_lock);
+ switch (desc->mode) {
+ case UBI_READONLY:
+ vol->readers -= 1;
+ break;
+ case UBI_READWRITE:
+ vol->writers -= 1;
+ break;
+ case UBI_EXCLUSIVE:
+ vol->exclusive = 0;
+ }
+ spin_unlock(&vol->ubi->volumes_lock);
+
+ kfree(desc);
+ module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL_GPL(ubi_close_volume);
+
+/**
+ * ubi_leb_read - read data.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number to read from
+ * @buf: buffer where to store the read data
+ * @offset: offset within the logical eraseblock to read from
+ * @len: how many bytes to read
+ * @check: whether UBI has to check the read data's CRC or not.
+ *
+ * This function reads data from offset @offset of logical eraseblock @lnum and
+ * stores the data at @buf. When reading from static volumes, @check specifies
+ * whether the data has to be checked or not. If yes, the whole logical
+ * eraseblock will be read and its CRC checksum will be checked (i.e., the CRC
+ * checksum is per-eraseblock). So checking may substantially slow down the
+ * read speed. The @check argument is ignored for dynamic volumes.
+ *
+ * In case of success, this function returns zero. In case of failure, this
+ * function returns a negative error code.
+ *
+ * %-EBADMSG error code is returned:
+ * o for both static and dynamic volumes if MTD driver has detected a data
+ * integrity problem (unrecoverable ECC checksum mismatch in case of NAND);
+ * o for static volumes in case of data CRC mismatch.
+ *
+ * If the volume is damaged because of an interrupted update this function just
+ * returns immediately with %-EBADF error code.
+ */
+int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
+ int len, int check)
+{
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ int err, vol_id = vol->vol_id;
+
+ dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset);
+
+ if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 ||
+ lnum >= vol->used_ebs || offset < 0 || len < 0 ||
+ offset + len > vol->usable_leb_size)
+ return -EINVAL;
+
+ if (vol->vol_type == UBI_STATIC_VOLUME && lnum == vol->used_ebs - 1 &&
+ offset + len > vol->last_eb_bytes)
+ return -EINVAL;
+
+ if (vol->upd_marker)
+ return -EBADF;
+ if (len == 0)
+ return 0;
+
+ err = ubi_eba_read_leb(ubi, vol_id, lnum, buf, offset, len, check);
+ if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) {
+ ubi_warn("mark volume %d as corrupted", vol_id);
+ vol->corrupted = 1;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ubi_leb_read);
+
+/**
+ * ubi_leb_write - write data.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number to write to
+ * @buf: data to write
+ * @offset: offset within the logical eraseblock where to write
+ * @len: how many bytes to write
+ * @dtype: expected data type
+ *
+ * This function writes @len bytes of data from @buf to offset @offset of
+ * logical eraseblock @lnum. The @dtype argument describes expected lifetime of
+ * the data.
+ *
+ * This function takes care of physical eraseblock write failures. If write to
+ * the physical eraseblock write operation fails, the logical eraseblock is
+ * re-mapped to another physical eraseblock, the data is recovered, and the
+ * write finishes. UBI has a pool of reserved physical eraseblocks for this.
+ *
+ * If all the data were successfully written, zero is returned. If an error
+ * occurred and UBI has not been able to recover from it, this function returns
+ * a negative error code. Note, in case of an error, it is possible that
+ * something was still written to the flash media, but that may be some
+ * garbage.
+ *
+ * If the volume is damaged because of an interrupted update this function just
+ * returns immediately with %-EBADF code.
+ */
+int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
+ int offset, int len, int dtype)
+{
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ int vol_id = vol->vol_id;
+
+ dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset);
+
+ if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
+ return -EINVAL;
+
+ if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
+ return -EROFS;
+
+ if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 ||
+ offset + len > vol->usable_leb_size || offset % ubi->min_io_size ||
+ len % ubi->min_io_size)
+ return -EINVAL;
+
+ if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
+ dtype != UBI_UNKNOWN)
+ return -EINVAL;
+
+ if (vol->upd_marker)
+ return -EBADF;
+
+ if (len == 0)
+ return 0;
+
+ return ubi_eba_write_leb(ubi, vol_id, lnum, buf, offset, len, dtype);
+}
+EXPORT_SYMBOL_GPL(ubi_leb_write);
+
+/*
+ * ubi_leb_change - change logical eraseblock atomically.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number to change
+ * @buf: data to write
+ * @len: how many bytes to write
+ * @dtype: expected data type
+ *
+ * This function changes the contents of a logical eraseblock atomically. @buf
+ * has to contain new logical eraseblock data, and @len - the length of the
+ * data, which has to be aligned. The length may be shorter then the logical
+ * eraseblock size, ant the logical eraseblock may be appended to more times
+ * later on. This function guarantees that in case of an unclean reboot the old
+ * contents is preserved. Returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
+ int len, int dtype)
+{
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ int vol_id = vol->vol_id;
+
+ dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum);
+
+ if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
+ return -EINVAL;
+
+ if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
+ return -EROFS;
+
+ if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 ||
+ len > vol->usable_leb_size || len % ubi->min_io_size)
+ return -EINVAL;
+
+ if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
+ dtype != UBI_UNKNOWN)
+ return -EINVAL;
+
+ if (vol->upd_marker)
+ return -EBADF;
+
+ if (len == 0)
+ return 0;
+
+ return ubi_eba_atomic_leb_change(ubi, vol_id, lnum, buf, len, dtype);
+}
+EXPORT_SYMBOL_GPL(ubi_leb_change);
+
+/**
+ * ubi_leb_erase - erase logical eraseblock.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number
+ *
+ * This function un-maps logical eraseblock @lnum and synchronously erases the
+ * correspondent physical eraseblock. Returns zero in case of success and a
+ * negative error code in case of failure.
+ *
+ * If the volume is damaged because of an interrupted update this function just
+ * returns immediately with %-EBADF code.
+ */
+int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
+{
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ int err, vol_id = vol->vol_id;
+
+ dbg_msg("erase LEB %d:%d", vol_id, lnum);
+
+ if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
+ return -EROFS;
+
+ if (lnum < 0 || lnum >= vol->reserved_pebs)
+ return -EINVAL;
+
+ if (vol->upd_marker)
+ return -EBADF;
+
+ err = ubi_eba_unmap_leb(ubi, vol_id, lnum);
+ if (err)
+ return err;
+
+ return ubi_wl_flush(ubi);
+}
+EXPORT_SYMBOL_GPL(ubi_leb_erase);
+
+/**
+ * ubi_leb_unmap - un-map logical eraseblock.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number
+ *
+ * This function un-maps logical eraseblock @lnum and schedules the
+ * corresponding physical eraseblock for erasure, so that it will eventually be
+ * physically erased in background. This operation is much faster then the
+ * erase operation.
+ *
+ * Unlike erase, the un-map operation does not guarantee that the logical
+ * eraseblock will contain all 0xFF bytes when UBI is initialized again. For
+ * example, if several logical eraseblocks are un-mapped, and an unclean reboot
+ * happens after this, the logical eraseblocks will not necessarily be
+ * un-mapped again when this MTD device is attached. They may actually be
+ * mapped to the same physical eraseblocks again. So, this function has to be
+ * used with care.
+ *
+ * In other words, when un-mapping a logical eraseblock, UBI does not store
+ * any information about this on the flash media, it just marks the logical
+ * eraseblock as "un-mapped" in RAM. If UBI is detached before the physical
+ * eraseblock is physically erased, it will be mapped again to the same logical
+ * eraseblock when the MTD device is attached again.
+ *
+ * The main and obvious use-case of this function is when the contents of a
+ * logical eraseblock has to be re-written. Then it is much more efficient to
+ * first un-map it, then write new data, rather then first erase it, then write
+ * new data. Note, once new data has been written to the logical eraseblock,
+ * UBI guarantees that the old contents has gone forever. In other words, if an
+ * unclean reboot happens after the logical eraseblock has been un-mapped and
+ * then written to, it will contain the last written data.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure. If the volume is damaged because of an interrupted update
+ * this function just returns immediately with %-EBADF code.
+ */
+int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
+{
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ int vol_id = vol->vol_id;
+
+ dbg_msg("unmap LEB %d:%d", vol_id, lnum);
+
+ if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
+ return -EROFS;
+
+ if (lnum < 0 || lnum >= vol->reserved_pebs)
+ return -EINVAL;
+
+ if (vol->upd_marker)
+ return -EBADF;
+
+ return ubi_eba_unmap_leb(ubi, vol_id, lnum);
+}
+EXPORT_SYMBOL_GPL(ubi_leb_unmap);
+
+/**
+ * ubi_is_mapped - check if logical eraseblock is mapped.
+ * @desc: volume descriptor
+ * @lnum: logical eraseblock number
+ *
+ * This function checks if logical eraseblock @lnum is mapped to a physical
+ * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily
+ * mean it will still be un-mapped after the UBI device is re-attached. The
+ * logical eraseblock may become mapped to the physical eraseblock it was last
+ * mapped to.
+ *
+ * This function returns %1 if the LEB is mapped, %0 if not, and a negative
+ * error code in case of failure. If the volume is damaged because of an
+ * interrupted update this function just returns immediately with %-EBADF error
+ * code.
+ */
+int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
+{
+ struct ubi_volume *vol = desc->vol;
+
+ dbg_msg("test LEB %d:%d", vol->vol_id, lnum);
+
+ if (lnum < 0 || lnum >= vol->reserved_pebs)
+ return -EINVAL;
+
+ if (vol->upd_marker)
+ return -EBADF;
+
+ return vol->eba_tbl[lnum] >= 0;
+}
+EXPORT_SYMBOL_GPL(ubi_is_mapped);
diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c
new file mode 100644
index 000000000000..38d4e6757dc7
--- /dev/null
+++ b/drivers/mtd/ubi/misc.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/* Here we keep miscellaneous functions which are used all over the UBI code */
+
+#include "ubi.h"
+
+/**
+ * calc_data_len - calculate how much real data is stored in a buffer.
+ * @ubi: UBI device description object
+ * @buf: a buffer with the contents of the physical eraseblock
+ * @length: the buffer length
+ *
+ * This function calculates how much "real data" is stored in @buf and returnes
+ * the length. Continuous 0xFF bytes at the end of the buffer are not
+ * considered as "real data".
+ */
+int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
+ int length)
+{
+ int i;
+
+ ubi_assert(length % ubi->min_io_size == 0);
+
+ for (i = length - 1; i >= 0; i--)
+ if (((const uint8_t *)buf)[i] != 0xFF)
+ break;
+
+ /* The resulting length must be aligned to the minimum flash I/O size */
+ length = ALIGN(i + 1, ubi->min_io_size);
+ return length;
+}
+
+/**
+ * ubi_check_volume - check the contents of a static volume.
+ * @ubi: UBI device description object
+ * @vol_id: ID of the volume to check
+ *
+ * This function checks if static volume @vol_id is corrupted by fully reading
+ * it and checking data CRC. This function returns %0 if the volume is not
+ * corrupted, %1 if it is corrupted and a negative error code in case of
+ * failure. Dynamic volumes are not checked and zero is returned immediately.
+ */
+int ubi_check_volume(struct ubi_device *ubi, int vol_id)
+{
+ void *buf;
+ int err = 0, i;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+
+ if (vol->vol_type != UBI_STATIC_VOLUME)
+ return 0;
+
+ buf = kmalloc(vol->usable_leb_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < vol->used_ebs; i++) {
+ int size;
+
+ if (i == vol->used_ebs - 1)
+ size = vol->last_eb_bytes;
+ else
+ size = vol->usable_leb_size;
+
+ err = ubi_eba_read_leb(ubi, vol_id, i, buf, 0, size, 1);
+ if (err) {
+ if (err == -EBADMSG)
+ err = 1;
+ break;
+ }
+ }
+
+ kfree(buf);
+ return err;
+}
+
+/**
+ * ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad
+ * eraseblock handling.
+ * @ubi: UBI device description object
+ */
+void ubi_calculate_reserved(struct ubi_device *ubi)
+{
+ ubi->beb_rsvd_level = ubi->good_peb_count/100;
+ ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE;
+ if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS)
+ ubi->beb_rsvd_level = MIN_RESEVED_PEBS;
+}
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
new file mode 100644
index 000000000000..473f3200b868
--- /dev/null
+++ b/drivers/mtd/ubi/scan.c
@@ -0,0 +1,1368 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * UBI scanning unit.
+ *
+ * This unit is responsible for scanning the flash media, checking UBI
+ * headers and providing complete information about the UBI flash image.
+ *
+ * The scanning information is reoresented by a &struct ubi_scan_info' object.
+ * Information about found volumes is represented by &struct ubi_scan_volume
+ * objects which are kept in volume RB-tree with root at the @volumes field.
+ * The RB-tree is indexed by the volume ID.
+ *
+ * Found logical eraseblocks are represented by &struct ubi_scan_leb objects.
+ * These objects are kept in per-volume RB-trees with the root at the
+ * corresponding &struct ubi_scan_volume object. To put it differently, we keep
+ * an RB-tree of per-volume objects and each of these objects is the root of
+ * RB-tree of per-eraseblock objects.
+ *
+ * Corrupted physical eraseblocks are put to the @corr list, free physical
+ * eraseblocks are put to the @free list and the physical eraseblock to be
+ * erased are put to the @erase list.
+ */
+
+#include <linux/err.h>
+#include <linux/crc32.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_si(const struct ubi_device *ubi,
+ struct ubi_scan_info *si);
+#else
+#define paranoid_check_si(ubi, si) 0
+#endif
+
+/* Temporary variables used during scanning */
+static struct ubi_ec_hdr *ech;
+static struct ubi_vid_hdr *vidh;
+
+int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec,
+ struct list_head *list)
+{
+ struct ubi_scan_leb *seb;
+
+ if (list == &si->free)
+ dbg_bld("add to free: PEB %d, EC %d", pnum, ec);
+ else if (list == &si->erase)
+ dbg_bld("add to erase: PEB %d, EC %d", pnum, ec);
+ else if (list == &si->corr)
+ dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec);
+ else if (list == &si->alien)
+ dbg_bld("add to alien: PEB %d, EC %d", pnum, ec);
+ else
+ BUG();
+
+ seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+ if (!seb)
+ return -ENOMEM;
+
+ seb->pnum = pnum;
+ seb->ec = ec;
+ list_add_tail(&seb->u.list, list);
+ return 0;
+}
+
+/**
+ * commit_to_mean_value - commit intermediate results to the final mean erase
+ * counter value.
+ * @si: scanning information
+ *
+ * This is a helper function which calculates partial mean erase counter mean
+ * value and adds it to the resulting mean value. As we can work only in
+ * integer arithmetic and we want to calculate the mean value of erase counter
+ * accurately, we first sum erase counter values in @si->ec_sum variable and
+ * count these components in @si->ec_count. If this temporary @si->ec_sum is
+ * going to overflow, we calculate the partial mean value
+ * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec.
+ */
+static void commit_to_mean_value(struct ubi_scan_info *si)
+{
+ si->ec_sum /= si->ec_count;
+ if (si->ec_sum % si->ec_count >= si->ec_count / 2)
+ si->mean_ec += 1;
+ si->mean_ec += si->ec_sum;
+}
+
+/**
+ * validate_vid_hdr - check that volume identifier header is correct and
+ * consistent.
+ * @vid_hdr: the volume identifier header to check
+ * @sv: information about the volume this logical eraseblock belongs to
+ * @pnum: physical eraseblock number the VID header came from
+ *
+ * This function checks that data stored in @vid_hdr is consistent. Returns
+ * non-zero if an inconsistency was found and zero if not.
+ *
+ * Note, UBI does sanity check of everything it reads from the flash media.
+ * Most of the checks are done in the I/O unit. Here we check that the
+ * information in the VID header is consistent to the information in other VID
+ * headers of the same volume.
+ */
+static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr,
+ const struct ubi_scan_volume *sv, int pnum)
+{
+ int vol_type = vid_hdr->vol_type;
+ int vol_id = ubi32_to_cpu(vid_hdr->vol_id);
+ int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs);
+ int data_pad = ubi32_to_cpu(vid_hdr->data_pad);
+
+ if (sv->leb_count != 0) {
+ int sv_vol_type;
+
+ /*
+ * This is not the first logical eraseblock belonging to this
+ * volume. Ensure that the data in its VID header is consistent
+ * to the data in previous logical eraseblock headers.
+ */
+
+ if (vol_id != sv->vol_id) {
+ dbg_err("inconsistent vol_id");
+ goto bad;
+ }
+
+ if (sv->vol_type == UBI_STATIC_VOLUME)
+ sv_vol_type = UBI_VID_STATIC;
+ else
+ sv_vol_type = UBI_VID_DYNAMIC;
+
+ if (vol_type != sv_vol_type) {
+ dbg_err("inconsistent vol_type");
+ goto bad;
+ }
+
+ if (used_ebs != sv->used_ebs) {
+ dbg_err("inconsistent used_ebs");
+ goto bad;
+ }
+
+ if (data_pad != sv->data_pad) {
+ dbg_err("inconsistent data_pad");
+ goto bad;
+ }
+ }
+
+ return 0;
+
+bad:
+ ubi_err("inconsistent VID header at PEB %d", pnum);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ ubi_dbg_dump_sv(sv);
+ return -EINVAL;
+}
+
+/**
+ * add_volume - add volume to the scanning information.
+ * @si: scanning information
+ * @vol_id: ID of the volume to add
+ * @pnum: physical eraseblock number
+ * @vid_hdr: volume identifier header
+ *
+ * If the volume corresponding to the @vid_hdr logical eraseblock is already
+ * present in the scanning information, this function does nothing. Otherwise
+ * it adds corresponding volume to the scanning information. Returns a pointer
+ * to the scanning volume object in case of success and a negative error code
+ * in case of failure.
+ */
+static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id,
+ int pnum,
+ const struct ubi_vid_hdr *vid_hdr)
+{
+ struct ubi_scan_volume *sv;
+ struct rb_node **p = &si->volumes.rb_node, *parent = NULL;
+
+ ubi_assert(vol_id == ubi32_to_cpu(vid_hdr->vol_id));
+
+ /* Walk the volume RB-tree to look if this volume is already present */
+ while (*p) {
+ parent = *p;
+ sv = rb_entry(parent, struct ubi_scan_volume, rb);
+
+ if (vol_id == sv->vol_id)
+ return sv;
+
+ if (vol_id > sv->vol_id)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ /* The volume is absent - add it */
+ sv = kmalloc(sizeof(struct ubi_scan_volume), GFP_KERNEL);
+ if (!sv)
+ return ERR_PTR(-ENOMEM);
+
+ sv->highest_lnum = sv->leb_count = 0;
+ si->max_sqnum = 0;
+ sv->vol_id = vol_id;
+ sv->root = RB_ROOT;
+ sv->used_ebs = ubi32_to_cpu(vid_hdr->used_ebs);
+ sv->data_pad = ubi32_to_cpu(vid_hdr->data_pad);
+ sv->compat = vid_hdr->compat;
+ sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME
+ : UBI_STATIC_VOLUME;
+ if (vol_id > si->highest_vol_id)
+ si->highest_vol_id = vol_id;
+
+ rb_link_node(&sv->rb, parent, p);
+ rb_insert_color(&sv->rb, &si->volumes);
+ si->vols_found += 1;
+ dbg_bld("added volume %d", vol_id);
+ return sv;
+}
+
+/**
+ * compare_lebs - find out which logical eraseblock is newer.
+ * @ubi: UBI device description object
+ * @seb: first logical eraseblock to compare
+ * @pnum: physical eraseblock number of the second logical eraseblock to
+ * compare
+ * @vid_hdr: volume identifier header of the second logical eraseblock
+ *
+ * This function compares 2 copies of a LEB and informs which one is newer. In
+ * case of success this function returns a positive value, in case of failure, a
+ * negative error code is returned. The success return codes use the following
+ * bits:
+ * o bit 0 is cleared: the first PEB (described by @seb) is newer then the
+ * second PEB (described by @pnum and @vid_hdr);
+ * o bit 0 is set: the second PEB is newer;
+ * o bit 1 is cleared: no bit-flips were detected in the newer LEB;
+ * o bit 1 is set: bit-flips were detected in the newer LEB;
+ * o bit 2 is cleared: the older LEB is not corrupted;
+ * o bit 2 is set: the older LEB is corrupted.
+ */
+static int compare_lebs(const struct ubi_device *ubi,
+ const struct ubi_scan_leb *seb, int pnum,
+ const struct ubi_vid_hdr *vid_hdr)
+{
+ void *buf;
+ int len, err, second_is_newer, bitflips = 0, corrupted = 0;
+ uint32_t data_crc, crc;
+ struct ubi_vid_hdr *vidh = NULL;
+ unsigned long long sqnum2 = ubi64_to_cpu(vid_hdr->sqnum);
+
+ if (seb->sqnum == 0 && sqnum2 == 0) {
+ long long abs, v1 = seb->leb_ver, v2 = ubi32_to_cpu(vid_hdr->leb_ver);
+
+ /*
+ * UBI constantly increases the logical eraseblock version
+ * number and it can overflow. Thus, we have to bear in mind
+ * that versions that are close to %0xFFFFFFFF are less then
+ * versions that are close to %0.
+ *
+ * The UBI WL unit guarantees that the number of pending tasks
+ * is not greater then %0x7FFFFFFF. So, if the difference
+ * between any two versions is greater or equivalent to
+ * %0x7FFFFFFF, there was an overflow and the logical
+ * eraseblock with lower version is actually newer then the one
+ * with higher version.
+ *
+ * FIXME: but this is anyway obsolete and will be removed at
+ * some point.
+ */
+
+ dbg_bld("using old crappy leb_ver stuff");
+
+ abs = v1 - v2;
+ if (abs < 0)
+ abs = -abs;
+
+ if (abs < 0x7FFFFFFF)
+ /* Non-overflow situation */
+ second_is_newer = (v2 > v1);
+ else
+ second_is_newer = (v2 < v1);
+ } else
+ /* Obviously the LEB with lower sequence counter is older */
+ second_is_newer = sqnum2 > seb->sqnum;
+
+ /*
+ * Now we know which copy is newer. If the copy flag of the PEB with
+ * newer version is not set, then we just return, otherwise we have to
+ * check data CRC. For the second PEB we already have the VID header,
+ * for the first one - we'll need to re-read it from flash.
+ *
+ * FIXME: this may be optimized so that we wouldn't read twice.
+ */
+
+ if (second_is_newer) {
+ if (!vid_hdr->copy_flag) {
+ /* It is not a copy, so it is newer */
+ dbg_bld("second PEB %d is newer, copy_flag is unset",
+ pnum);
+ return 1;
+ }
+ } else {
+ pnum = seb->pnum;
+
+ vidh = ubi_zalloc_vid_hdr(ubi);
+ if (!vidh)
+ return -ENOMEM;
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0);
+ if (err) {
+ if (err == UBI_IO_BITFLIPS)
+ bitflips = 1;
+ else {
+ dbg_err("VID of PEB %d header is bad, but it "
+ "was OK earlier", pnum);
+ if (err > 0)
+ err = -EIO;
+
+ goto out_free_vidh;
+ }
+ }
+
+ if (!vidh->copy_flag) {
+ /* It is not a copy, so it is newer */
+ dbg_bld("first PEB %d is newer, copy_flag is unset",
+ pnum);
+ err = bitflips << 1;
+ goto out_free_vidh;
+ }
+
+ vid_hdr = vidh;
+ }
+
+ /* Read the data of the copy and check the CRC */
+
+ len = ubi32_to_cpu(vid_hdr->data_size);
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out_free_vidh;
+ }
+
+ err = ubi_io_read_data(ubi, buf, pnum, 0, len);
+ if (err && err != UBI_IO_BITFLIPS)
+ goto out_free_buf;
+
+ data_crc = ubi32_to_cpu(vid_hdr->data_crc);
+ crc = crc32(UBI_CRC32_INIT, buf, len);
+ if (crc != data_crc) {
+ dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x",
+ pnum, crc, data_crc);
+ corrupted = 1;
+ bitflips = 0;
+ second_is_newer = !second_is_newer;
+ } else {
+ dbg_bld("PEB %d CRC is OK", pnum);
+ bitflips = !!err;
+ }
+
+ kfree(buf);
+ ubi_free_vid_hdr(ubi, vidh);
+
+ if (second_is_newer)
+ dbg_bld("second PEB %d is newer, copy_flag is set", pnum);
+ else
+ dbg_bld("first PEB %d is newer, copy_flag is set", pnum);
+
+ return second_is_newer | (bitflips << 1) | (corrupted << 2);
+
+out_free_buf:
+ kfree(buf);
+out_free_vidh:
+ ubi_free_vid_hdr(ubi, vidh);
+ ubi_assert(err < 0);
+ return err;
+}
+
+/**
+ * ubi_scan_add_used - add information about a physical eraseblock to the
+ * scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: the physical eraseblock number
+ * @ec: erase counter
+ * @vid_hdr: the volume identifier header
+ * @bitflips: if bit-flips were detected when this physical eraseblock was read
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si,
+ int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
+ int bitflips)
+{
+ int err, vol_id, lnum;
+ uint32_t leb_ver;
+ unsigned long long sqnum;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *seb;
+ struct rb_node **p, *parent = NULL;
+
+ vol_id = ubi32_to_cpu(vid_hdr->vol_id);
+ lnum = ubi32_to_cpu(vid_hdr->lnum);
+ sqnum = ubi64_to_cpu(vid_hdr->sqnum);
+ leb_ver = ubi32_to_cpu(vid_hdr->leb_ver);
+
+ dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d",
+ pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips);
+
+ sv = add_volume(si, vol_id, pnum, vid_hdr);
+ if (IS_ERR(sv) < 0)
+ return PTR_ERR(sv);
+
+ /*
+ * Walk the RB-tree of logical eraseblocks of volume @vol_id to look
+ * if this is the first instance of this logical eraseblock or not.
+ */
+ p = &sv->root.rb_node;
+ while (*p) {
+ int cmp_res;
+
+ parent = *p;
+ seb = rb_entry(parent, struct ubi_scan_leb, u.rb);
+ if (lnum != seb->lnum) {
+ if (lnum < seb->lnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ continue;
+ }
+
+ /*
+ * There is already a physical eraseblock describing the same
+ * logical eraseblock present.
+ */
+
+ dbg_bld("this LEB already exists: PEB %d, sqnum %llu, "
+ "LEB ver %u, EC %d", seb->pnum, seb->sqnum,
+ seb->leb_ver, seb->ec);
+
+ /*
+ * Make sure that the logical eraseblocks have different
+ * versions. Otherwise the image is bad.
+ */
+ if (seb->leb_ver == leb_ver && leb_ver != 0) {
+ ubi_err("two LEBs with same version %u", leb_ver);
+ ubi_dbg_dump_seb(seb, 0);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure that the logical eraseblocks have different
+ * sequence numbers. Otherwise the image is bad.
+ *
+ * FIXME: remove 'sqnum != 0' check when leb_ver is removed.
+ */
+ if (seb->sqnum == sqnum && sqnum != 0) {
+ ubi_err("two LEBs with same sequence number %llu",
+ sqnum);
+ ubi_dbg_dump_seb(seb, 0);
+ ubi_dbg_dump_vid_hdr(vid_hdr);
+ return -EINVAL;
+ }
+
+ /*
+ * Now we have to drop the older one and preserve the newer
+ * one.
+ */
+ cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr);
+ if (cmp_res < 0)
+ return cmp_res;
+
+ if (cmp_res & 1) {
+ /*
+ * This logical eraseblock is newer then the one
+ * found earlier.
+ */
+ err = validate_vid_hdr(vid_hdr, sv, pnum);
+ if (err)
+ return err;
+
+ if (cmp_res & 4)
+ err = ubi_scan_add_to_list(si, seb->pnum,
+ seb->ec, &si->corr);
+ else
+ err = ubi_scan_add_to_list(si, seb->pnum,
+ seb->ec, &si->erase);
+ if (err)
+ return err;
+
+ seb->ec = ec;
+ seb->pnum = pnum;
+ seb->scrub = ((cmp_res & 2) || bitflips);
+ seb->sqnum = sqnum;
+ seb->leb_ver = leb_ver;
+
+ if (sv->highest_lnum == lnum)
+ sv->last_data_size =
+ ubi32_to_cpu(vid_hdr->data_size);
+
+ return 0;
+ } else {
+ /*
+ * This logical eraseblock is older then the one found
+ * previously.
+ */
+ if (cmp_res & 4)
+ return ubi_scan_add_to_list(si, pnum, ec,
+ &si->corr);
+ else
+ return ubi_scan_add_to_list(si, pnum, ec,
+ &si->erase);
+ }
+ }
+
+ /*
+ * We've met this logical eraseblock for the first time, add it to the
+ * scanning information.
+ */
+
+ err = validate_vid_hdr(vid_hdr, sv, pnum);
+ if (err)
+ return err;
+
+ seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+ if (!seb)
+ return -ENOMEM;
+
+ seb->ec = ec;
+ seb->pnum = pnum;
+ seb->lnum = lnum;
+ seb->sqnum = sqnum;
+ seb->scrub = bitflips;
+ seb->leb_ver = leb_ver;
+
+ if (sv->highest_lnum <= lnum) {
+ sv->highest_lnum = lnum;
+ sv->last_data_size = ubi32_to_cpu(vid_hdr->data_size);
+ }
+
+ if (si->max_sqnum < sqnum)
+ si->max_sqnum = sqnum;
+
+ sv->leb_count += 1;
+ rb_link_node(&seb->u.rb, parent, p);
+ rb_insert_color(&seb->u.rb, &sv->root);
+ return 0;
+}
+
+/**
+ * ubi_scan_find_sv - find information about a particular volume in the
+ * scanning information.
+ * @si: scanning information
+ * @vol_id: the requested volume ID
+ *
+ * This function returns a pointer to the volume description or %NULL if there
+ * are no data about this volume in the scanning information.
+ */
+struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
+ int vol_id)
+{
+ struct ubi_scan_volume *sv;
+ struct rb_node *p = si->volumes.rb_node;
+
+ while (p) {
+ sv = rb_entry(p, struct ubi_scan_volume, rb);
+
+ if (vol_id == sv->vol_id)
+ return sv;
+
+ if (vol_id > sv->vol_id)
+ p = p->rb_left;
+ else
+ p = p->rb_right;
+ }
+
+ return NULL;
+}
+
+/**
+ * ubi_scan_find_seb - find information about a particular logical
+ * eraseblock in the volume scanning information.
+ * @sv: a pointer to the volume scanning information
+ * @lnum: the requested logical eraseblock
+ *
+ * This function returns a pointer to the scanning logical eraseblock or %NULL
+ * if there are no data about it in the scanning volume information.
+ */
+struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
+ int lnum)
+{
+ struct ubi_scan_leb *seb;
+ struct rb_node *p = sv->root.rb_node;
+
+ while (p) {
+ seb = rb_entry(p, struct ubi_scan_leb, u.rb);
+
+ if (lnum == seb->lnum)
+ return seb;
+
+ if (lnum > seb->lnum)
+ p = p->rb_left;
+ else
+ p = p->rb_right;
+ }
+
+ return NULL;
+}
+
+/**
+ * ubi_scan_rm_volume - delete scanning information about a volume.
+ * @si: scanning information
+ * @sv: the volume scanning information to delete
+ */
+void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv)
+{
+ struct rb_node *rb;
+ struct ubi_scan_leb *seb;
+
+ dbg_bld("remove scanning information about volume %d", sv->vol_id);
+
+ while ((rb = rb_first(&sv->root))) {
+ seb = rb_entry(rb, struct ubi_scan_leb, u.rb);
+ rb_erase(&seb->u.rb, &sv->root);
+ list_add_tail(&seb->u.list, &si->erase);
+ }
+
+ rb_erase(&sv->rb, &si->volumes);
+ kfree(sv);
+ si->vols_found -= 1;
+}
+
+/**
+ * ubi_scan_erase_peb - erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: physical eraseblock number to erase;
+ * @ec: erase counter value to write (%UBI_SCAN_UNKNOWN_EC if it is unknown)
+ *
+ * This function erases physical eraseblock 'pnum', and writes the erase
+ * counter header to it. This function should only be used on UBI device
+ * initialization stages, when the EBA unit had not been yet initialized. This
+ * function returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+int ubi_scan_erase_peb(const struct ubi_device *ubi,
+ const struct ubi_scan_info *si, int pnum, int ec)
+{
+ int err;
+ struct ubi_ec_hdr *ec_hdr;
+
+ ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!ec_hdr)
+ return -ENOMEM;
+
+ if ((long long)ec >= UBI_MAX_ERASECOUNTER) {
+ /*
+ * Erase counter overflow. Upgrade UBI and use 64-bit
+ * erase counters internally.
+ */
+ ubi_err("erase counter overflow at PEB %d, EC %d", pnum, ec);
+ return -EINVAL;
+ }
+
+ ec_hdr->ec = cpu_to_ubi64(ec);
+
+ err = ubi_io_sync_erase(ubi, pnum, 0);
+ if (err < 0)
+ goto out_free;
+
+ err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr);
+
+out_free:
+ kfree(ec_hdr);
+ return err;
+}
+
+/**
+ * ubi_scan_get_free_peb - get a free physical eraseblock.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns a free physical eraseblock. It is supposed to be
+ * called on the UBI initialization stages when the wear-leveling unit is not
+ * initialized yet. This function picks a physical eraseblocks from one of the
+ * lists, writes the EC header if it is needed, and removes it from the list.
+ *
+ * This function returns scanning physical eraseblock information in case of
+ * success and an error code in case of failure.
+ */
+struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi,
+ struct ubi_scan_info *si)
+{
+ int err = 0, i;
+ struct ubi_scan_leb *seb;
+
+ if (!list_empty(&si->free)) {
+ seb = list_entry(si->free.next, struct ubi_scan_leb, u.list);
+ list_del(&seb->u.list);
+ dbg_bld("return free PEB %d, EC %d", seb->pnum, seb->ec);
+ return seb;
+ }
+
+ for (i = 0; i < 2; i++) {
+ struct list_head *head;
+ struct ubi_scan_leb *tmp_seb;
+
+ if (i == 0)
+ head = &si->erase;
+ else
+ head = &si->corr;
+
+ /*
+ * We try to erase the first physical eraseblock from the @head
+ * list and pick it if we succeed, or try to erase the
+ * next one if not. And so forth. We don't want to take care
+ * about bad eraseblocks here - they'll be handled later.
+ */
+ list_for_each_entry_safe(seb, tmp_seb, head, u.list) {
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+
+ err = ubi_scan_erase_peb(ubi, si, seb->pnum, seb->ec+1);
+ if (err)
+ continue;
+
+ seb->ec += 1;
+ list_del(&seb->u.list);
+ dbg_bld("return PEB %d, EC %d", seb->pnum, seb->ec);
+ return seb;
+ }
+ }
+
+ ubi_err("no eraseblocks found");
+ return ERR_PTR(-ENOSPC);
+}
+
+/**
+ * process_eb - read UBI headers, check them and add corresponding data
+ * to the scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @pnum: the physical eraseblock number
+ *
+ * This function returns a zero if the physical eraseblock was succesfully
+ * handled and a negative error code in case of failure.
+ */
+static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum)
+{
+ long long ec;
+ int err, bitflips = 0, vol_id, ec_corr = 0;
+
+ dbg_bld("scan PEB %d", pnum);
+
+ /* Skip bad physical eraseblocks */
+ err = ubi_io_is_bad(ubi, pnum);
+ if (err < 0)
+ return err;
+ else if (err) {
+ /*
+ * FIXME: this is actually duty of the I/O unit to initialize
+ * this, but MTD does not provide enough information.
+ */
+ si->bad_peb_count += 1;
+ return 0;
+ }
+
+ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
+ if (err < 0)
+ return err;
+ else if (err == UBI_IO_BITFLIPS)
+ bitflips = 1;
+ else if (err == UBI_IO_PEB_EMPTY)
+ return ubi_scan_add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC,
+ &si->erase);
+ else if (err == UBI_IO_BAD_EC_HDR) {
+ /*
+ * We have to also look at the VID header, possibly it is not
+ * corrupted. Set %bitflips flag in order to make this PEB be
+ * moved and EC be re-created.
+ */
+ ec_corr = 1;
+ ec = UBI_SCAN_UNKNOWN_EC;
+ bitflips = 1;
+ }
+
+ si->is_empty = 0;
+
+ if (!ec_corr) {
+ /* Make sure UBI version is OK */
+ if (ech->version != UBI_VERSION) {
+ ubi_err("this UBI version is %d, image version is %d",
+ UBI_VERSION, (int)ech->version);
+ return -EINVAL;
+ }
+
+ ec = ubi64_to_cpu(ech->ec);
+ if (ec > UBI_MAX_ERASECOUNTER) {
+ /*
+ * Erase counter overflow. The EC headers have 64 bits
+ * reserved, but we anyway make use of only 31 bit
+ * values, as this seems to be enough for any existing
+ * flash. Upgrade UBI and use 64-bit erase counters
+ * internally.
+ */
+ ubi_err("erase counter overflow, max is %d",
+ UBI_MAX_ERASECOUNTER);
+ ubi_dbg_dump_ec_hdr(ech);
+ return -EINVAL;
+ }
+ }
+
+ /* OK, we've done with the EC header, let's look at the VID header */
+
+ err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0);
+ if (err < 0)
+ return err;
+ else if (err == UBI_IO_BITFLIPS)
+ bitflips = 1;
+ else if (err == UBI_IO_BAD_VID_HDR ||
+ (err == UBI_IO_PEB_FREE && ec_corr)) {
+ /* VID header is corrupted */
+ err = ubi_scan_add_to_list(si, pnum, ec, &si->corr);
+ if (err)
+ return err;
+ goto adjust_mean_ec;
+ } else if (err == UBI_IO_PEB_FREE) {
+ /* No VID header - the physical eraseblock is free */
+ err = ubi_scan_add_to_list(si, pnum, ec, &si->free);
+ if (err)
+ return err;
+ goto adjust_mean_ec;
+ }
+
+ vol_id = ubi32_to_cpu(vidh->vol_id);
+ if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOL_ID) {
+ int lnum = ubi32_to_cpu(vidh->lnum);
+
+ /* Unsupported internal volume */
+ switch (vidh->compat) {
+ case UBI_COMPAT_DELETE:
+ ubi_msg("\"delete\" compatible internal volume %d:%d"
+ " found, remove it", vol_id, lnum);
+ err = ubi_scan_add_to_list(si, pnum, ec, &si->corr);
+ if (err)
+ return err;
+ break;
+
+ case UBI_COMPAT_RO:
+ ubi_msg("read-only compatible internal volume %d:%d"
+ " found, switch to read-only mode",
+ vol_id, lnum);
+ ubi->ro_mode = 1;
+ break;
+
+ case UBI_COMPAT_PRESERVE:
+ ubi_msg("\"preserve\" compatible internal volume %d:%d"
+ " found", vol_id, lnum);
+ err = ubi_scan_add_to_list(si, pnum, ec, &si->alien);
+ if (err)
+ return err;
+ si->alien_peb_count += 1;
+ return 0;
+
+ case UBI_COMPAT_REJECT:
+ ubi_err("incompatible internal volume %d:%d found",
+ vol_id, lnum);
+ return -EINVAL;
+ }
+ }
+
+ /* Both UBI headers seem to be fine */
+ err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips);
+ if (err)
+ return err;
+
+adjust_mean_ec:
+ if (!ec_corr) {
+ if (si->ec_sum + ec < ec) {
+ commit_to_mean_value(si);
+ si->ec_sum = 0;
+ si->ec_count = 0;
+ } else {
+ si->ec_sum += ec;
+ si->ec_count += 1;
+ }
+
+ if (ec > si->max_ec)
+ si->max_ec = ec;
+ if (ec < si->min_ec)
+ si->min_ec = ec;
+ }
+
+ return 0;
+}
+
+/**
+ * ubi_scan - scan an MTD device.
+ * @ubi: UBI device description object
+ *
+ * This function does full scanning of an MTD device and returns complete
+ * information about it. In case of failure, an error code is returned.
+ */
+struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
+{
+ int err, pnum;
+ struct rb_node *rb1, *rb2;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *seb;
+ struct ubi_scan_info *si;
+
+ si = kzalloc(sizeof(struct ubi_scan_info), GFP_KERNEL);
+ if (!si)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&si->corr);
+ INIT_LIST_HEAD(&si->free);
+ INIT_LIST_HEAD(&si->erase);
+ INIT_LIST_HEAD(&si->alien);
+ si->volumes = RB_ROOT;
+ si->is_empty = 1;
+
+ err = -ENOMEM;
+ ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!ech)
+ goto out_si;
+
+ vidh = ubi_zalloc_vid_hdr(ubi);
+ if (!vidh)
+ goto out_ech;
+
+ for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+ cond_resched();
+
+ dbg_msg("process PEB %d", pnum);
+ err = process_eb(ubi, si, pnum);
+ if (err < 0)
+ goto out_vidh;
+ }
+
+ dbg_msg("scanning is finished");
+
+ /* Finish mean erase counter calculations */
+ if (si->ec_count)
+ commit_to_mean_value(si);
+
+ if (si->is_empty)
+ ubi_msg("empty MTD device detected");
+
+ /*
+ * In case of unknown erase counter we use the mean erase counter
+ * value.
+ */
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+ }
+
+ list_for_each_entry(seb, &si->free, u.list) {
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+ }
+
+ list_for_each_entry(seb, &si->corr, u.list)
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+
+ list_for_each_entry(seb, &si->erase, u.list)
+ if (seb->ec == UBI_SCAN_UNKNOWN_EC)
+ seb->ec = si->mean_ec;
+
+ err = paranoid_check_si(ubi, si);
+ if (err) {
+ if (err > 0)
+ err = -EINVAL;
+ goto out_vidh;
+ }
+
+ ubi_free_vid_hdr(ubi, vidh);
+ kfree(ech);
+
+ return si;
+
+out_vidh:
+ ubi_free_vid_hdr(ubi, vidh);
+out_ech:
+ kfree(ech);
+out_si:
+ ubi_scan_destroy_si(si);
+ return ERR_PTR(err);
+}
+
+/**
+ * destroy_sv - free the scanning volume information
+ * @sv: scanning volume information
+ *
+ * This function destroys the volume RB-tree (@sv->root) and the scanning
+ * volume information.
+ */
+static void destroy_sv(struct ubi_scan_volume *sv)
+{
+ struct ubi_scan_leb *seb;
+ struct rb_node *this = sv->root.rb_node;
+
+ while (this) {
+ if (this->rb_left)
+ this = this->rb_left;
+ else if (this->rb_right)
+ this = this->rb_right;
+ else {
+ seb = rb_entry(this, struct ubi_scan_leb, u.rb);
+ this = rb_parent(this);
+ if (this) {
+ if (this->rb_left == &seb->u.rb)
+ this->rb_left = NULL;
+ else
+ this->rb_right = NULL;
+ }
+
+ kfree(seb);
+ }
+ }
+ kfree(sv);
+}
+
+/**
+ * ubi_scan_destroy_si - destroy scanning information.
+ * @si: scanning information
+ */
+void ubi_scan_destroy_si(struct ubi_scan_info *si)
+{
+ struct ubi_scan_leb *seb, *seb_tmp;
+ struct ubi_scan_volume *sv;
+ struct rb_node *rb;
+
+ list_for_each_entry_safe(seb, seb_tmp, &si->alien, u.list) {
+ list_del(&seb->u.list);
+ kfree(seb);
+ }
+ list_for_each_entry_safe(seb, seb_tmp, &si->erase, u.list) {
+ list_del(&seb->u.list);
+ kfree(seb);
+ }
+ list_for_each_entry_safe(seb, seb_tmp, &si->corr, u.list) {
+ list_del(&seb->u.list);
+ kfree(seb);
+ }
+ list_for_each_entry_safe(seb, seb_tmp, &si->free, u.list) {
+ list_del(&seb->u.list);
+ kfree(seb);
+ }
+
+ /* Destroy the volume RB-tree */
+ rb = si->volumes.rb_node;
+ while (rb) {
+ if (rb->rb_left)
+ rb = rb->rb_left;
+ else if (rb->rb_right)
+ rb = rb->rb_right;
+ else {
+ sv = rb_entry(rb, struct ubi_scan_volume, rb);
+
+ rb = rb_parent(rb);
+ if (rb) {
+ if (rb->rb_left == &sv->rb)
+ rb->rb_left = NULL;
+ else
+ rb->rb_right = NULL;
+ }
+
+ destroy_sv(sv);
+ }
+ }
+
+ kfree(si);
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_si - check if the scanning information is correct and
+ * consistent.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero if the scanning information is all right, %1 if
+ * not and a negative error code if an error occurred.
+ */
+static int paranoid_check_si(const struct ubi_device *ubi,
+ struct ubi_scan_info *si)
+{
+ int pnum, err, vols_found = 0;
+ struct rb_node *rb1, *rb2;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *seb, *last_seb;
+ uint8_t *buf;
+
+ /*
+ * At first, check that scanning information is ok.
+ */
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+ int leb_count = 0;
+
+ cond_resched();
+
+ vols_found += 1;
+
+ if (si->is_empty) {
+ ubi_err("bad is_empty flag");
+ goto bad_sv;
+ }
+
+ if (sv->vol_id < 0 || sv->highest_lnum < 0 ||
+ sv->leb_count < 0 || sv->vol_type < 0 || sv->used_ebs < 0 ||
+ sv->data_pad < 0 || sv->last_data_size < 0) {
+ ubi_err("negative values");
+ goto bad_sv;
+ }
+
+ if (sv->vol_id >= UBI_MAX_VOLUMES &&
+ sv->vol_id < UBI_INTERNAL_VOL_START) {
+ ubi_err("bad vol_id");
+ goto bad_sv;
+ }
+
+ if (sv->vol_id > si->highest_vol_id) {
+ ubi_err("highest_vol_id is %d, but vol_id %d is there",
+ si->highest_vol_id, sv->vol_id);
+ goto out;
+ }
+
+ if (sv->vol_type != UBI_DYNAMIC_VOLUME &&
+ sv->vol_type != UBI_STATIC_VOLUME) {
+ ubi_err("bad vol_type");
+ goto bad_sv;
+ }
+
+ if (sv->data_pad > ubi->leb_size / 2) {
+ ubi_err("bad data_pad");
+ goto bad_sv;
+ }
+
+ last_seb = NULL;
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+ cond_resched();
+
+ last_seb = seb;
+ leb_count += 1;
+
+ if (seb->pnum < 0 || seb->ec < 0) {
+ ubi_err("negative values");
+ goto bad_seb;
+ }
+
+ if (seb->ec < si->min_ec) {
+ ubi_err("bad si->min_ec (%d), %d found",
+ si->min_ec, seb->ec);
+ goto bad_seb;
+ }
+
+ if (seb->ec > si->max_ec) {
+ ubi_err("bad si->max_ec (%d), %d found",
+ si->max_ec, seb->ec);
+ goto bad_seb;
+ }
+
+ if (seb->pnum >= ubi->peb_count) {
+ ubi_err("too high PEB number %d, total PEBs %d",
+ seb->pnum, ubi->peb_count);
+ goto bad_seb;
+ }
+
+ if (sv->vol_type == UBI_STATIC_VOLUME) {
+ if (seb->lnum >= sv->used_ebs) {
+ ubi_err("bad lnum or used_ebs");
+ goto bad_seb;
+ }
+ } else {
+ if (sv->used_ebs != 0) {
+ ubi_err("non-zero used_ebs");
+ goto bad_seb;
+ }
+ }
+
+ if (seb->lnum > sv->highest_lnum) {
+ ubi_err("incorrect highest_lnum or lnum");
+ goto bad_seb;
+ }
+ }
+
+ if (sv->leb_count != leb_count) {
+ ubi_err("bad leb_count, %d objects in the tree",
+ leb_count);
+ goto bad_sv;
+ }
+
+ if (!last_seb)
+ continue;
+
+ seb = last_seb;
+
+ if (seb->lnum != sv->highest_lnum) {
+ ubi_err("bad highest_lnum");
+ goto bad_seb;
+ }
+ }
+
+ if (vols_found != si->vols_found) {
+ ubi_err("bad si->vols_found %d, should be %d",
+ si->vols_found, vols_found);
+ goto out;
+ }
+
+ /* Check that scanning information is correct */
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+ last_seb = NULL;
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+ int vol_type;
+
+ cond_resched();
+
+ last_seb = seb;
+
+ err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1);
+ if (err && err != UBI_IO_BITFLIPS) {
+ ubi_err("VID header is not OK (%d)", err);
+ if (err > 0)
+ err = -EIO;
+ return err;
+ }
+
+ vol_type = vidh->vol_type == UBI_VID_DYNAMIC ?
+ UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME;
+ if (sv->vol_type != vol_type) {
+ ubi_err("bad vol_type");
+ goto bad_vid_hdr;
+ }
+
+ if (seb->sqnum != ubi64_to_cpu(vidh->sqnum)) {
+ ubi_err("bad sqnum %llu", seb->sqnum);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->vol_id != ubi32_to_cpu(vidh->vol_id)) {
+ ubi_err("bad vol_id %d", sv->vol_id);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->compat != vidh->compat) {
+ ubi_err("bad compat %d", vidh->compat);
+ goto bad_vid_hdr;
+ }
+
+ if (seb->lnum != ubi32_to_cpu(vidh->lnum)) {
+ ubi_err("bad lnum %d", seb->lnum);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->used_ebs != ubi32_to_cpu(vidh->used_ebs)) {
+ ubi_err("bad used_ebs %d", sv->used_ebs);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->data_pad != ubi32_to_cpu(vidh->data_pad)) {
+ ubi_err("bad data_pad %d", sv->data_pad);
+ goto bad_vid_hdr;
+ }
+
+ if (seb->leb_ver != ubi32_to_cpu(vidh->leb_ver)) {
+ ubi_err("bad leb_ver %u", seb->leb_ver);
+ goto bad_vid_hdr;
+ }
+ }
+
+ if (!last_seb)
+ continue;
+
+ if (sv->highest_lnum != ubi32_to_cpu(vidh->lnum)) {
+ ubi_err("bad highest_lnum %d", sv->highest_lnum);
+ goto bad_vid_hdr;
+ }
+
+ if (sv->last_data_size != ubi32_to_cpu(vidh->data_size)) {
+ ubi_err("bad last_data_size %d", sv->last_data_size);
+ goto bad_vid_hdr;
+ }
+ }
+
+ /*
+ * Make sure that all the physical eraseblocks are in one of the lists
+ * or trees.
+ */
+ buf = kmalloc(ubi->peb_count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memset(buf, 1, ubi->peb_count);
+ for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+ err = ubi_io_is_bad(ubi, pnum);
+ if (err < 0)
+ return err;
+ else if (err)
+ buf[pnum] = 0;
+ }
+
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb)
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)
+ buf[seb->pnum] = 0;
+
+ list_for_each_entry(seb, &si->free, u.list)
+ buf[seb->pnum] = 0;
+
+ list_for_each_entry(seb, &si->corr, u.list)
+ buf[seb->pnum] = 0;
+
+ list_for_each_entry(seb, &si->erase, u.list)
+ buf[seb->pnum] = 0;
+
+ list_for_each_entry(seb, &si->alien, u.list)
+ buf[seb->pnum] = 0;
+
+ err = 0;
+ for (pnum = 0; pnum < ubi->peb_count; pnum++)
+ if (buf[pnum]) {
+ ubi_err("PEB %d is not referred", pnum);
+ err = 1;
+ }
+
+ kfree(buf);
+ if (err)
+ goto out;
+ return 0;
+
+bad_seb:
+ ubi_err("bad scanning information about LEB %d", seb->lnum);
+ ubi_dbg_dump_seb(seb, 0);
+ ubi_dbg_dump_sv(sv);
+ goto out;
+
+bad_sv:
+ ubi_err("bad scanning information about volume %d", sv->vol_id);
+ ubi_dbg_dump_sv(sv);
+ goto out;
+
+bad_vid_hdr:
+ ubi_err("bad scanning information about volume %d", sv->vol_id);
+ ubi_dbg_dump_sv(sv);
+ ubi_dbg_dump_vid_hdr(vidh);
+
+out:
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h
new file mode 100644
index 000000000000..3949f6192c76
--- /dev/null
+++ b/drivers/mtd/ubi/scan.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_SCAN_H__
+#define __UBI_SCAN_H__
+
+/* The erase counter value for this physical eraseblock is unknown */
+#define UBI_SCAN_UNKNOWN_EC (-1)
+
+/**
+ * struct ubi_scan_leb - scanning information about a physical eraseblock.
+ * @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown)
+ * @pnum: physical eraseblock number
+ * @lnum: logical eraseblock number
+ * @scrub: if this physical eraseblock needs scrubbing
+ * @sqnum: sequence number
+ * @u: unions RB-tree or @list links
+ * @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects
+ * @u.list: link in one of the eraseblock lists
+ * @leb_ver: logical eraseblock version (obsolete)
+ *
+ * One object of this type is allocated for each physical eraseblock during
+ * scanning.
+ */
+struct ubi_scan_leb {
+ int ec;
+ int pnum;
+ int lnum;
+ int scrub;
+ unsigned long long sqnum;
+ union {
+ struct rb_node rb;
+ struct list_head list;
+ } u;
+ uint32_t leb_ver;
+};
+
+/**
+ * struct ubi_scan_volume - scanning information about a volume.
+ * @vol_id: volume ID
+ * @highest_lnum: highest logical eraseblock number in this volume
+ * @leb_count: number of logical eraseblocks in this volume
+ * @vol_type: volume type
+ * @used_ebs: number of used logical eraseblocks in this volume (only for
+ * static volumes)
+ * @last_data_size: amount of data in the last logical eraseblock of this
+ * volume (always equivalent to the usable logical eraseblock size in case of
+ * dynamic volumes)
+ * @data_pad: how many bytes at the end of logical eraseblocks of this volume
+ * are not used (due to volume alignment)
+ * @compat: compatibility flags of this volume
+ * @rb: link in the volume RB-tree
+ * @root: root of the RB-tree containing all the eraseblock belonging to this
+ * volume (&struct ubi_scan_leb objects)
+ *
+ * One object of this type is allocated for each volume during scanning.
+ */
+struct ubi_scan_volume {
+ int vol_id;
+ int highest_lnum;
+ int leb_count;
+ int vol_type;
+ int used_ebs;
+ int last_data_size;
+ int data_pad;
+ int compat;
+ struct rb_node rb;
+ struct rb_root root;
+};
+
+/**
+ * struct ubi_scan_info - UBI scanning information.
+ * @volumes: root of the volume RB-tree
+ * @corr: list of corrupted physical eraseblocks
+ * @free: list of free physical eraseblocks
+ * @erase: list of physical eraseblocks which have to be erased
+ * @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
+ * @bad_peb_count: count of bad physical eraseblocks
+ * those belonging to "preserve"-compatible internal volumes)
+ * @vols_found: number of volumes found during scanning
+ * @highest_vol_id: highest volume ID
+ * @alien_peb_count: count of physical eraseblocks in the @alien list
+ * @is_empty: flag indicating whether the MTD device is empty or not
+ * @min_ec: lowest erase counter value
+ * @max_ec: highest erase counter value
+ * @max_sqnum: highest sequence number value
+ * @mean_ec: mean erase counter value
+ * @ec_sum: a temporary variable used when calculating @mean_ec
+ * @ec_count: a temporary variable used when calculating @mean_ec
+ *
+ * This data structure contains the result of scanning and may be used by other
+ * UBI units to build final UBI data structures, further error-recovery and so
+ * on.
+ */
+struct ubi_scan_info {
+ struct rb_root volumes;
+ struct list_head corr;
+ struct list_head free;
+ struct list_head erase;
+ struct list_head alien;
+ int bad_peb_count;
+ int vols_found;
+ int highest_vol_id;
+ int alien_peb_count;
+ int is_empty;
+ int min_ec;
+ int max_ec;
+ unsigned long long max_sqnum;
+ int mean_ec;
+ int ec_sum;
+ int ec_count;
+};
+
+struct ubi_device;
+struct ubi_vid_hdr;
+
+/*
+ * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a
+ * list.
+ *
+ * @sv: volume scanning information
+ * @seb: scanning eraseblock infprmation
+ * @list: the list to move to
+ */
+static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv,
+ struct ubi_scan_leb *seb,
+ struct list_head *list)
+{
+ rb_erase(&seb->u.rb, &sv->root);
+ list_add_tail(&seb->u.list, list);
+}
+
+int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec,
+ struct list_head *list);
+int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si,
+ int pnum, int ec, const struct ubi_vid_hdr *vid_hdr,
+ int bitflips);
+struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
+ int vol_id);
+struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv,
+ int lnum);
+void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv);
+struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi,
+ struct ubi_scan_info *si);
+int ubi_scan_erase_peb(const struct ubi_device *ubi,
+ const struct ubi_scan_info *si, int pnum, int ec);
+struct ubi_scan_info *ubi_scan(struct ubi_device *ubi);
+void ubi_scan_destroy_si(struct ubi_scan_info *si);
+
+#endif /* !__UBI_SCAN_H__ */
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
new file mode 100644
index 000000000000..feb647f108f0
--- /dev/null
+++ b/drivers/mtd/ubi/ubi.h
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_UBI_H__
+#define __UBI_UBI_H__
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/mtd/mtd.h>
+
+#include <mtd/ubi-header.h>
+#include <linux/mtd/ubi.h>
+
+#include "scan.h"
+#include "debug.h"
+
+/* Maximum number of supported UBI devices */
+#define UBI_MAX_DEVICES 32
+
+/* UBI name used for character devices, sysfs, etc */
+#define UBI_NAME_STR "ubi"
+
+/* Normal UBI messages */
+#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__)
+/* UBI warning messages */
+#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \
+ __FUNCTION__, ##__VA_ARGS__)
+/* UBI error messages */
+#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \
+ __FUNCTION__, ##__VA_ARGS__)
+
+/* Lowest number PEBs reserved for bad PEB handling */
+#define MIN_RESEVED_PEBS 2
+
+/* Background thread name pattern */
+#define UBI_BGT_NAME_PATTERN "ubi_bgt%dd"
+
+/* This marker in the EBA table means that the LEB is um-mapped */
+#define UBI_LEB_UNMAPPED -1
+
+/*
+ * In case of errors, UBI tries to repeat the operation several times before
+ * returning error. The below constant defines how many times UBI re-tries.
+ */
+#define UBI_IO_RETRIES 3
+
+/*
+ * Error codes returned by the I/O unit.
+ *
+ * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only
+ * 0xFF bytes
+ * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a
+ * valid erase counter header, and the rest are %0xFF bytes
+ * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC)
+ * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or
+ * CRC)
+ * UBI_IO_BITFLIPS: bit-flips were detected and corrected
+ */
+enum {
+ UBI_IO_PEB_EMPTY = 1,
+ UBI_IO_PEB_FREE,
+ UBI_IO_BAD_EC_HDR,
+ UBI_IO_BAD_VID_HDR,
+ UBI_IO_BITFLIPS
+};
+
+extern int ubi_devices_cnt;
+extern struct ubi_device *ubi_devices[];
+
+struct ubi_volume_desc;
+
+/**
+ * struct ubi_volume - UBI volume description data structure.
+ * @dev: device object to make use of the the Linux device model
+ * @cdev: character device object to create character device
+ * @ubi: reference to the UBI device description object
+ * @vol_id: volume ID
+ * @readers: number of users holding this volume in read-only mode
+ * @writers: number of users holding this volume in read-write mode
+ * @exclusive: whether somebody holds this volume in exclusive mode
+ * @removed: if the volume was removed
+ * @checked: if this static volume was checked
+ *
+ * @reserved_pebs: how many physical eraseblocks are reserved for this volume
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @usable_leb_size: logical eraseblock size without padding
+ * @used_ebs: how many logical eraseblocks in this volume contain data
+ * @last_eb_bytes: how many bytes are stored in the last logical eraseblock
+ * @used_bytes: how many bytes of data this volume contains
+ * @upd_marker: non-zero if the update marker is set for this volume
+ * @corrupted: non-zero if the volume is corrupted (static volumes only)
+ * @alignment: volume alignment
+ * @data_pad: how many bytes are not used at the end of physical eraseblocks to
+ * satisfy the requested alignment
+ * @name_len: volume name length
+ * @name: volume name
+ *
+ * @updating: whether the volume is being updated
+ * @upd_ebs: how many eraseblocks are expected to be updated
+ * @upd_bytes: how many bytes are expected to be received
+ * @upd_received: how many update bytes were already received
+ * @upd_buf: update buffer which is used to collect update data
+ *
+ * @eba_tbl: EBA table of this volume (LEB->PEB mapping)
+ *
+ * @gluebi_desc: gluebi UBI volume descriptor
+ * @gluebi_refcount: reference count of the gluebi MTD device
+ * @gluebi_mtd: MTD device description object of the gluebi MTD device
+ *
+ * The @corrupted field indicates that the volume's contents is corrupted.
+ * Since UBI protects only static volumes, this field is not relevant to
+ * dynamic volumes - it is user's responsibility to assure their data
+ * integrity.
+ *
+ * The @upd_marker flag indicates that this volume is either being updated at
+ * the moment or is damaged because of an unclean reboot.
+ */
+struct ubi_volume {
+ struct device dev;
+ struct cdev cdev;
+ struct ubi_device *ubi;
+ int vol_id;
+ int readers;
+ int writers;
+ int exclusive;
+ int removed;
+ int checked;
+
+ int reserved_pebs;
+ int vol_type;
+ int usable_leb_size;
+ int used_ebs;
+ int last_eb_bytes;
+ long long used_bytes;
+ int upd_marker;
+ int corrupted;
+ int alignment;
+ int data_pad;
+ int name_len;
+ char name[UBI_VOL_NAME_MAX+1];
+
+ int updating;
+ int upd_ebs;
+ long long upd_bytes;
+ long long upd_received;
+ void *upd_buf;
+
+ int *eba_tbl;
+
+#ifdef CONFIG_MTD_UBI_GLUEBI
+ /* Gluebi-related stuff may be compiled out */
+ struct ubi_volume_desc *gluebi_desc;
+ int gluebi_refcount;
+ struct mtd_info gluebi_mtd;
+#endif
+};
+
+/**
+ * struct ubi_volume_desc - descriptor of the UBI volume returned when it is
+ * opened.
+ * @vol: reference to the corresponding volume description object
+ * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE)
+ */
+struct ubi_volume_desc {
+ struct ubi_volume *vol;
+ int mode;
+};
+
+struct ubi_wl_entry;
+
+/**
+ * struct ubi_device - UBI device description structure
+ * @dev: class device object to use the the Linux device model
+ * @cdev: character device object to create character device
+ * @ubi_num: UBI device number
+ * @ubi_name: UBI device name
+ * @major: character device major number
+ * @vol_count: number of volumes in this UBI device
+ * @volumes: volumes of this UBI device
+ * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs,
+ * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, @vol->readers,
+ * @vol->writers, @vol->exclusive, @vol->removed, @vol->mapping and
+ * @vol->eba_tbl.
+ *
+ * @rsvd_pebs: count of reserved physical eraseblocks
+ * @avail_pebs: count of available physical eraseblocks
+ * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB
+ * handling
+ * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling
+ *
+ * @vtbl_slots: how many slots are available in the volume table
+ * @vtbl_size: size of the volume table in bytes
+ * @vtbl: in-RAM volume table copy
+ *
+ * @max_ec: current highest erase counter value
+ * @mean_ec: current mean erase counter value
+ *
+ * global_sqnum: global sequence number
+ * @ltree_lock: protects the lock tree and @global_sqnum
+ * @ltree: the lock tree
+ * @vtbl_mutex: protects on-flash volume table
+ *
+ * @used: RB-tree of used physical eraseblocks
+ * @free: RB-tree of free physical eraseblocks
+ * @scrub: RB-tree of physical eraseblocks which need scrubbing
+ * @prot: protection trees
+ * @prot.pnum: protection tree indexed by physical eraseblock numbers
+ * @prot.aec: protection tree indexed by absolute erase counter value
+ * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from,
+ * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works
+ * fields
+ * @wl_scheduled: non-zero if the wear-leveling was scheduled
+ * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any
+ * physical eraseblock
+ * @abs_ec: absolute erase counter
+ * @move_from: physical eraseblock from where the data is being moved
+ * @move_to: physical eraseblock where the data is being moved to
+ * @move_from_put: if the "from" PEB was put
+ * @move_to_put: if the "to" PEB was put
+ * @works: list of pending works
+ * @works_count: count of pending works
+ * @bgt_thread: background thread description object
+ * @thread_enabled: if the background thread is enabled
+ * @bgt_name: background thread name
+ *
+ * @flash_size: underlying MTD device size (in bytes)
+ * @peb_count: count of physical eraseblocks on the MTD device
+ * @peb_size: physical eraseblock size
+ * @bad_peb_count: count of bad physical eraseblocks
+ * @good_peb_count: count of good physical eraseblocks
+ * @min_io_size: minimal input/output unit size of the underlying MTD device
+ * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers
+ * @ro_mode: if the UBI device is in read-only mode
+ * @leb_size: logical eraseblock size
+ * @leb_start: starting offset of logical eraseblocks within physical
+ * eraseblocks
+ * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size
+ * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size
+ * @vid_hdr_offset: starting offset of the volume identifier header (might be
+ * unaligned)
+ * @vid_hdr_aloffset: starting offset of the VID header aligned to
+ * @hdrs_min_io_size
+ * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
+ * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
+ * not
+ * @mtd: MTD device descriptor
+ */
+struct ubi_device {
+ struct cdev cdev;
+ struct device dev;
+ int ubi_num;
+ char ubi_name[sizeof(UBI_NAME_STR)+5];
+ int major;
+ int vol_count;
+ struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
+ spinlock_t volumes_lock;
+
+ int rsvd_pebs;
+ int avail_pebs;
+ int beb_rsvd_pebs;
+ int beb_rsvd_level;
+
+ int vtbl_slots;
+ int vtbl_size;
+ struct ubi_vtbl_record *vtbl;
+ struct mutex vtbl_mutex;
+
+ int max_ec;
+ int mean_ec;
+
+ /* EBA unit's stuff */
+ unsigned long long global_sqnum;
+ spinlock_t ltree_lock;
+ struct rb_root ltree;
+
+ /* Wear-leveling unit's stuff */
+ struct rb_root used;
+ struct rb_root free;
+ struct rb_root scrub;
+ struct {
+ struct rb_root pnum;
+ struct rb_root aec;
+ } prot;
+ spinlock_t wl_lock;
+ int wl_scheduled;
+ struct ubi_wl_entry **lookuptbl;
+ unsigned long long abs_ec;
+ struct ubi_wl_entry *move_from;
+ struct ubi_wl_entry *move_to;
+ int move_from_put;
+ int move_to_put;
+ struct list_head works;
+ int works_count;
+ struct task_struct *bgt_thread;
+ int thread_enabled;
+ char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
+
+ /* I/O unit's stuff */
+ long long flash_size;
+ int peb_count;
+ int peb_size;
+ int bad_peb_count;
+ int good_peb_count;
+ int min_io_size;
+ int hdrs_min_io_size;
+ int ro_mode;
+ int leb_size;
+ int leb_start;
+ int ec_hdr_alsize;
+ int vid_hdr_alsize;
+ int vid_hdr_offset;
+ int vid_hdr_aloffset;
+ int vid_hdr_shift;
+ int bad_allowed;
+ struct mtd_info *mtd;
+};
+
+extern struct file_operations ubi_cdev_operations;
+extern struct file_operations ubi_vol_cdev_operations;
+extern struct class *ubi_class;
+
+/* vtbl.c */
+int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
+ struct ubi_vtbl_record *vtbl_rec);
+int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si);
+
+/* vmt.c */
+int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req);
+int ubi_remove_volume(struct ubi_volume_desc *desc);
+int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs);
+int ubi_add_volume(struct ubi_device *ubi, int vol_id);
+void ubi_free_volume(struct ubi_device *ubi, int vol_id);
+
+/* upd.c */
+int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes);
+int ubi_more_update_data(struct ubi_device *ubi, int vol_id,
+ const void __user *buf, int count);
+
+/* misc.c */
+int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length);
+int ubi_check_volume(struct ubi_device *ubi, int vol_id);
+void ubi_calculate_reserved(struct ubi_device *ubi);
+
+/* gluebi.c */
+#ifdef CONFIG_MTD_UBI_GLUEBI
+int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol);
+int ubi_destroy_gluebi(struct ubi_volume *vol);
+#else
+#define ubi_create_gluebi(ubi, vol) 0
+#define ubi_destroy_gluebi(vol) 0
+#endif
+
+/* eba.c */
+int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum);
+int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf,
+ int offset, int len, int check);
+int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum,
+ const void *buf, int offset, int len, int dtype);
+int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum,
+ const void *buf, int len, int dtype,
+ int used_ebs);
+int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum,
+ const void *buf, int len, int dtype);
+int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
+ struct ubi_vid_hdr *vid_hdr);
+int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
+void ubi_eba_close(const struct ubi_device *ubi);
+
+/* wl.c */
+int ubi_wl_get_peb(struct ubi_device *ubi, int dtype);
+int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture);
+int ubi_wl_flush(struct ubi_device *ubi);
+int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum);
+int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
+void ubi_wl_close(struct ubi_device *ubi);
+
+/* io.c */
+int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
+ int len);
+int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum,
+ int offset, int len);
+int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture);
+int ubi_io_is_bad(const struct ubi_device *ubi, int pnum);
+int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum);
+int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum,
+ struct ubi_ec_hdr *ec_hdr, int verbose);
+int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum,
+ struct ubi_ec_hdr *ec_hdr);
+int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum,
+ struct ubi_vid_hdr *vid_hdr, int verbose);
+int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum,
+ struct ubi_vid_hdr *vid_hdr);
+
+/*
+ * ubi_rb_for_each_entry - walk an RB-tree.
+ * @rb: a pointer to type 'struct rb_node' to to use as a loop counter
+ * @pos: a pointer to RB-tree entry type to use as a loop counter
+ * @root: RB-tree's root
+ * @member: the name of the 'struct rb_node' within the RB-tree entry
+ */
+#define ubi_rb_for_each_entry(rb, pos, root, member) \
+ for (rb = rb_first(root), \
+ pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \
+ rb; \
+ rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member))
+
+/**
+ * ubi_zalloc_vid_hdr - allocate a volume identifier header object.
+ * @ubi: UBI device description object
+ *
+ * This function returns a pointer to the newly allocated and zero-filled
+ * volume identifier header object in case of success and %NULL in case of
+ * failure.
+ */
+static inline struct ubi_vid_hdr *ubi_zalloc_vid_hdr(const struct ubi_device *ubi)
+{
+ void *vid_hdr;
+
+ vid_hdr = kzalloc(ubi->vid_hdr_alsize, GFP_KERNEL);
+ if (!vid_hdr)
+ return NULL;
+
+ /*
+ * VID headers may be stored at un-aligned flash offsets, so we shift
+ * the pointer.
+ */
+ return vid_hdr + ubi->vid_hdr_shift;
+}
+
+/**
+ * ubi_free_vid_hdr - free a volume identifier header object.
+ * @ubi: UBI device description object
+ * @vid_hdr: the object to free
+ */
+static inline void ubi_free_vid_hdr(const struct ubi_device *ubi,
+ struct ubi_vid_hdr *vid_hdr)
+{
+ void *p = vid_hdr;
+
+ if (!p)
+ return;
+
+ kfree(p - ubi->vid_hdr_shift);
+}
+
+/*
+ * This function is equivalent to 'ubi_io_read()', but @offset is relative to
+ * the beginning of the logical eraseblock, not to the beginning of the
+ * physical eraseblock.
+ */
+static inline int ubi_io_read_data(const struct ubi_device *ubi, void *buf,
+ int pnum, int offset, int len)
+{
+ ubi_assert(offset >= 0);
+ return ubi_io_read(ubi, buf, pnum, offset + ubi->leb_start, len);
+}
+
+/*
+ * This function is equivalent to 'ubi_io_write()', but @offset is relative to
+ * the beginning of the logical eraseblock, not to the beginning of the
+ * physical eraseblock.
+ */
+static inline int ubi_io_write_data(const struct ubi_device *ubi, const void *buf,
+ int pnum, int offset, int len)
+{
+ ubi_assert(offset >= 0);
+ return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len);
+}
+
+/**
+ * ubi_ro_mode - switch to read-only mode.
+ * @ubi: UBI device description object
+ */
+static inline void ubi_ro_mode(struct ubi_device *ubi)
+{
+ ubi->ro_mode = 1;
+ ubi_warn("switch to read-only mode");
+}
+
+/**
+ * vol_id2idx - get table index by volume ID.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ */
+static inline int vol_id2idx(const struct ubi_device *ubi, int vol_id)
+{
+ if (vol_id >= UBI_INTERNAL_VOL_START)
+ return vol_id - UBI_INTERNAL_VOL_START + ubi->vtbl_slots;
+ else
+ return vol_id;
+}
+
+/**
+ * idx2vol_id - get volume ID by table index.
+ * @ubi: UBI device description object
+ * @idx: table index
+ */
+static inline int idx2vol_id(const struct ubi_device *ubi, int idx)
+{
+ if (idx >= ubi->vtbl_slots)
+ return idx - ubi->vtbl_slots + UBI_INTERNAL_VOL_START;
+ else
+ return idx;
+}
+
+#endif /* !__UBI_UBI_H__ */
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
new file mode 100644
index 000000000000..8925b977e3dc
--- /dev/null
+++ b/drivers/mtd/ubi/upd.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ *
+ * Jan 2007: Alexander Schmidt, hacked per-volume update.
+ */
+
+/*
+ * This file contains implementation of the volume update functionality.
+ *
+ * The update operation is based on the per-volume update marker which is
+ * stored in the volume table. The update marker is set before the update
+ * starts, and removed after the update has been finished. So if the update was
+ * interrupted by an unclean re-boot or due to some other reasons, the update
+ * marker stays on the flash media and UBI finds it when it attaches the MTD
+ * device next time. If the update marker is set for a volume, the volume is
+ * treated as damaged and most I/O operations are prohibited. Only a new update
+ * operation is allowed.
+ *
+ * Note, in general it is possible to implement the update operation as a
+ * transaction with a roll-back capability.
+ */
+
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+/**
+ * set_update_marker - set update marker.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function sets the update marker flag for volume @vol_id. Returns zero
+ * in case of success and a negative error code in case of failure.
+ */
+static int set_update_marker(struct ubi_device *ubi, int vol_id)
+{
+ int err;
+ struct ubi_vtbl_record vtbl_rec;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+
+ dbg_msg("set update marker for volume %d", vol_id);
+
+ if (vol->upd_marker) {
+ ubi_assert(ubi->vtbl[vol_id].upd_marker);
+ dbg_msg("already set");
+ return 0;
+ }
+
+ memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record));
+ vtbl_rec.upd_marker = 1;
+
+ err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+ vol->upd_marker = 1;
+ return err;
+}
+
+/**
+ * clear_update_marker - clear update marker.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @bytes: new data size in bytes
+ *
+ * This function clears the update marker for volume @vol_id, sets new volume
+ * data size and clears the "corrupted" flag (static volumes only). Returns
+ * zero in case of success and a negative error code in case of failure.
+ */
+static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long bytes)
+{
+ int err;
+ uint64_t tmp;
+ struct ubi_vtbl_record vtbl_rec;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+
+ dbg_msg("clear update marker for volume %d", vol_id);
+
+ memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record));
+ ubi_assert(vol->upd_marker && vtbl_rec.upd_marker);
+ vtbl_rec.upd_marker = 0;
+
+ if (vol->vol_type == UBI_STATIC_VOLUME) {
+ vol->corrupted = 0;
+ vol->used_bytes = tmp = bytes;
+ vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size);
+ vol->used_ebs = tmp;
+ if (vol->last_eb_bytes)
+ vol->used_ebs += 1;
+ else
+ vol->last_eb_bytes = vol->usable_leb_size;
+ }
+
+ err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+ vol->upd_marker = 0;
+ return err;
+}
+
+/**
+ * ubi_start_update - start volume update.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @bytes: update bytes
+ *
+ * This function starts volume update operation. If @bytes is zero, the volume
+ * is just wiped out. Returns zero in case of success and a negative error code
+ * in case of failure.
+ */
+int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes)
+{
+ int i, err;
+ uint64_t tmp;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+
+ dbg_msg("start update of volume %d, %llu bytes", vol_id, bytes);
+ vol->updating = 1;
+
+ err = set_update_marker(ubi, vol_id);
+ if (err)
+ return err;
+
+ /* Before updating - wipe out the volume */
+ for (i = 0; i < vol->reserved_pebs; i++) {
+ err = ubi_eba_unmap_leb(ubi, vol_id, i);
+ if (err)
+ return err;
+ }
+
+ if (bytes == 0) {
+ err = clear_update_marker(ubi, vol_id, 0);
+ if (err)
+ return err;
+ err = ubi_wl_flush(ubi);
+ if (!err)
+ vol->updating = 0;
+ }
+
+ vol->upd_buf = kmalloc(ubi->leb_size, GFP_KERNEL);
+ if (!vol->upd_buf)
+ return -ENOMEM;
+
+ tmp = bytes;
+ vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size);
+ vol->upd_ebs += tmp;
+ vol->upd_bytes = bytes;
+ vol->upd_received = 0;
+ return 0;
+}
+
+/**
+ * write_leb - write update data.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ * @lnum: logical eraseblock number
+ * @buf: data to write
+ * @len: data size
+ * @used_ebs: how many logical eraseblocks will this volume contain (static
+ * volumes only)
+ *
+ * This function writes update data to corresponding logical eraseblock. In
+ * case of dynamic volume, this function checks if the data contains 0xFF bytes
+ * at the end. If yes, the 0xFF bytes are cut and not written. So if the whole
+ * buffer contains only 0xFF bytes, the LEB is left unmapped.
+ *
+ * The reason why we skip the trailing 0xFF bytes in case of dynamic volume is
+ * that we want to make sure that more data may be appended to the logical
+ * eraseblock in future. Indeed, writing 0xFF bytes may have side effects and
+ * this PEB won't be writable anymore. So if one writes the file-system image
+ * to the UBI volume where 0xFFs mean free space - UBI makes sure this free
+ * space is writable after the update.
+ *
+ * We do not do this for static volumes because they are read-only. But this
+ * also cannot be done because we have to store per-LEB CRC and the correct
+ * data length.
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf,
+ int len, int used_ebs)
+{
+ int err, l;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+ l = ALIGN(len, ubi->min_io_size);
+ memset(buf + len, 0xFF, l - len);
+
+ l = ubi_calc_data_len(ubi, buf, l);
+ if (l == 0) {
+ dbg_msg("all %d bytes contain 0xFF - skip", len);
+ return 0;
+ }
+ if (len != l)
+ dbg_msg("skip last %d bytes (0xFF)", len - l);
+
+ err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l,
+ UBI_UNKNOWN);
+ } else {
+ /*
+ * When writing static volume, and this is the last logical
+ * eraseblock, the length (@len) does not have to be aligned to
+ * the minimal flash I/O unit. The 'ubi_eba_write_leb_st()'
+ * function accepts exact (unaligned) length and stores it in
+ * the VID header. And it takes care of proper alignment by
+ * padding the buffer. Here we just make sure the padding will
+ * contain zeros, not random trash.
+ */
+ memset(buf + len, 0, vol->usable_leb_size - len);
+ err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len,
+ UBI_UNKNOWN, used_ebs);
+ }
+
+ return err;
+}
+
+/**
+ * ubi_more_update_data - write more update data.
+ * @vol: volume description object
+ * @buf: write data (user-space memory buffer)
+ * @count: how much bytes to write
+ *
+ * This function writes more data to the volume which is being updated. It may
+ * be called arbitrary number of times until all of the update data arrive.
+ * This function returns %0 in case of success, number of bytes written during
+ * the last call if the whole volume update was successfully finished, and a
+ * negative error code in case of failure.
+ */
+int ubi_more_update_data(struct ubi_device *ubi, int vol_id,
+ const void __user *buf, int count)
+{
+ uint64_t tmp;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+ int lnum, offs, err = 0, len, to_write = count;
+
+ dbg_msg("write %d of %lld bytes, %lld already passed",
+ count, vol->upd_bytes, vol->upd_received);
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ tmp = vol->upd_received;
+ offs = do_div(tmp, vol->usable_leb_size);
+ lnum = tmp;
+
+ if (vol->upd_received + count > vol->upd_bytes)
+ to_write = count = vol->upd_bytes - vol->upd_received;
+
+ /*
+ * When updating volumes, we accumulate whole logical eraseblock of
+ * data and write it at once.
+ */
+ if (offs != 0) {
+ /*
+ * This is a write to the middle of the logical eraseblock. We
+ * copy the data to our update buffer and wait for more data or
+ * flush it if the whole eraseblock is written or the update
+ * is finished.
+ */
+
+ len = vol->usable_leb_size - offs;
+ if (len > count)
+ len = count;
+
+ err = copy_from_user(vol->upd_buf + offs, buf, len);
+ if (err)
+ return -EFAULT;
+
+ if (offs + len == vol->usable_leb_size ||
+ vol->upd_received + len == vol->upd_bytes) {
+ int flush_len = offs + len;
+
+ /*
+ * OK, we gathered either the whole eraseblock or this
+ * is the last chunk, it's time to flush the buffer.
+ */
+ ubi_assert(flush_len <= vol->usable_leb_size);
+ err = write_leb(ubi, vol_id, lnum, vol->upd_buf,
+ flush_len, vol->upd_ebs);
+ if (err)
+ return err;
+ }
+
+ vol->upd_received += len;
+ count -= len;
+ buf += len;
+ lnum += 1;
+ }
+
+ /*
+ * If we've got more to write, let's continue. At this point we know we
+ * are starting from the beginning of an eraseblock.
+ */
+ while (count) {
+ if (count > vol->usable_leb_size)
+ len = vol->usable_leb_size;
+ else
+ len = count;
+
+ err = copy_from_user(vol->upd_buf, buf, len);
+ if (err)
+ return -EFAULT;
+
+ if (len == vol->usable_leb_size ||
+ vol->upd_received + len == vol->upd_bytes) {
+ err = write_leb(ubi, vol_id, lnum, vol->upd_buf, len,
+ vol->upd_ebs);
+ if (err)
+ break;
+ }
+
+ vol->upd_received += len;
+ count -= len;
+ lnum += 1;
+ buf += len;
+ }
+
+ ubi_assert(vol->upd_received <= vol->upd_bytes);
+ if (vol->upd_received == vol->upd_bytes) {
+ /* The update is finished, clear the update marker */
+ err = clear_update_marker(ubi, vol_id, vol->upd_bytes);
+ if (err)
+ return err;
+ err = ubi_wl_flush(ubi);
+ if (err == 0) {
+ err = to_write;
+ kfree(vol->upd_buf);
+ vol->updating = 0;
+ }
+ }
+
+ return err;
+}
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
new file mode 100644
index 000000000000..622d0d18952c
--- /dev/null
+++ b/drivers/mtd/ubi/vmt.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file contains implementation of volume creation, deletion, updating and
+ * resizing.
+ */
+
+#include <linux/err.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static void paranoid_check_volumes(struct ubi_device *ubi);
+#else
+#define paranoid_check_volumes(ubi)
+#endif
+
+static ssize_t vol_attribute_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+/* Device attributes corresponding to files in '/<sysfs>/class/ubi/ubiX_Y' */
+static struct device_attribute vol_reserved_ebs =
+ __ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_type =
+ __ATTR(type, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_name =
+ __ATTR(name, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_corrupted =
+ __ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_alignment =
+ __ATTR(alignment, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_usable_eb_size =
+ __ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_data_bytes =
+ __ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL);
+static struct device_attribute vol_upd_marker =
+ __ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL);
+
+/*
+ * "Show" method for files in '/<sysfs>/class/ubi/ubiX_Y/'.
+ *
+ * Consider a situation:
+ * A. process 1 opens a sysfs file related to volume Y, say
+ * /<sysfs>/class/ubi/ubiX_Y/reserved_ebs;
+ * B. process 2 removes volume Y;
+ * C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file;
+ *
+ * What we want to do in a situation like that is to return error when the file
+ * is read. This is done by means of the 'removed' flag and the 'vol_lock' of
+ * the UBI volume description object.
+ */
+static ssize_t vol_attribute_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
+
+ spin_lock(&vol->ubi->volumes_lock);
+ if (vol->removed) {
+ spin_unlock(&vol->ubi->volumes_lock);
+ return -ENODEV;
+ }
+ if (attr == &vol_reserved_ebs)
+ ret = sprintf(buf, "%d\n", vol->reserved_pebs);
+ else if (attr == &vol_type) {
+ const char *tp;
+ tp = vol->vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static";
+ ret = sprintf(buf, "%s\n", tp);
+ } else if (attr == &vol_name)
+ ret = sprintf(buf, "%s\n", vol->name);
+ else if (attr == &vol_corrupted)
+ ret = sprintf(buf, "%d\n", vol->corrupted);
+ else if (attr == &vol_alignment)
+ ret = sprintf(buf, "%d\n", vol->alignment);
+ else if (attr == &vol_usable_eb_size) {
+ ret = sprintf(buf, "%d\n", vol->usable_leb_size);
+ } else if (attr == &vol_data_bytes)
+ ret = sprintf(buf, "%lld\n", vol->used_bytes);
+ else if (attr == &vol_upd_marker)
+ ret = sprintf(buf, "%d\n", vol->upd_marker);
+ else
+ BUG();
+ spin_unlock(&vol->ubi->volumes_lock);
+ return ret;
+}
+
+/* Release method for volume devices */
+static void vol_release(struct device *dev)
+{
+ struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
+ ubi_assert(vol->removed);
+ kfree(vol);
+}
+
+/**
+ * volume_sysfs_init - initialize sysfs for new volume.
+ * @ubi: UBI device description object
+ * @vol: volume description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ *
+ * Note, this function does not free allocated resources in case of failure -
+ * the caller does it. This is because this would cause release() here and the
+ * caller would oops.
+ */
+static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol)
+{
+ int err;
+
+ err = device_create_file(&vol->dev, &vol_reserved_ebs);
+ if (err)
+ return err;
+ err = device_create_file(&vol->dev, &vol_type);
+ if (err)
+ return err;
+ err = device_create_file(&vol->dev, &vol_name);
+ if (err)
+ return err;
+ err = device_create_file(&vol->dev, &vol_corrupted);
+ if (err)
+ return err;
+ err = device_create_file(&vol->dev, &vol_alignment);
+ if (err)
+ return err;
+ err = device_create_file(&vol->dev, &vol_usable_eb_size);
+ if (err)
+ return err;
+ err = device_create_file(&vol->dev, &vol_data_bytes);
+ if (err)
+ return err;
+ err = device_create_file(&vol->dev, &vol_upd_marker);
+ if (err)
+ return err;
+ return 0;
+}
+
+/**
+ * volume_sysfs_close - close sysfs for a volume.
+ * @vol: volume description object
+ */
+static void volume_sysfs_close(struct ubi_volume *vol)
+{
+ device_remove_file(&vol->dev, &vol_upd_marker);
+ device_remove_file(&vol->dev, &vol_data_bytes);
+ device_remove_file(&vol->dev, &vol_usable_eb_size);
+ device_remove_file(&vol->dev, &vol_alignment);
+ device_remove_file(&vol->dev, &vol_corrupted);
+ device_remove_file(&vol->dev, &vol_name);
+ device_remove_file(&vol->dev, &vol_type);
+ device_remove_file(&vol->dev, &vol_reserved_ebs);
+ device_unregister(&vol->dev);
+}
+
+/**
+ * ubi_create_volume - create volume.
+ * @ubi: UBI device description object
+ * @req: volume creation request
+ *
+ * This function creates volume described by @req. If @req->vol_id id
+ * %UBI_VOL_NUM_AUTO, this function automatically assigne ID to the new volume
+ * and saves it in @req->vol_id. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
+{
+ int i, err, vol_id = req->vol_id;
+ struct ubi_volume *vol;
+ struct ubi_vtbl_record vtbl_rec;
+ uint64_t bytes;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL);
+ if (!vol)
+ return -ENOMEM;
+
+ spin_lock(&ubi->volumes_lock);
+
+ if (vol_id == UBI_VOL_NUM_AUTO) {
+ /* Find unused volume ID */
+ dbg_msg("search for vacant volume ID");
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (!ubi->volumes[i]) {
+ vol_id = i;
+ break;
+ }
+
+ if (vol_id == UBI_VOL_NUM_AUTO) {
+ dbg_err("out of volume IDs");
+ err = -ENFILE;
+ goto out_unlock;
+ }
+ req->vol_id = vol_id;
+ }
+
+ dbg_msg("volume ID %d, %llu bytes, type %d, name %s",
+ vol_id, (unsigned long long)req->bytes,
+ (int)req->vol_type, req->name);
+
+ /* Ensure that this volume does not exist */
+ err = -EEXIST;
+ if (ubi->volumes[vol_id]) {
+ dbg_err("volume %d already exists", vol_id);
+ goto out_unlock;
+ }
+
+ /* Ensure that the name is unique */
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i] &&
+ ubi->volumes[i]->name_len == req->name_len &&
+ strcmp(ubi->volumes[i]->name, req->name) == 0) {
+ dbg_err("volume \"%s\" exists (ID %d)", req->name, i);
+ goto out_unlock;
+ }
+
+ /* Calculate how many eraseblocks are requested */
+ vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
+ bytes = req->bytes;
+ if (do_div(bytes, vol->usable_leb_size))
+ vol->reserved_pebs = 1;
+ vol->reserved_pebs += bytes;
+
+ /* Reserve physical eraseblocks */
+ if (vol->reserved_pebs > ubi->avail_pebs) {
+ dbg_err("not enough PEBs, only %d available", ubi->avail_pebs);
+ spin_unlock(&ubi->volumes_lock);
+ err = -ENOSPC;
+ goto out_unlock;
+ }
+ ubi->avail_pebs -= vol->reserved_pebs;
+ ubi->rsvd_pebs += vol->reserved_pebs;
+
+ vol->vol_id = vol_id;
+ vol->alignment = req->alignment;
+ vol->data_pad = ubi->leb_size % vol->alignment;
+ vol->vol_type = req->vol_type;
+ vol->name_len = req->name_len;
+ memcpy(vol->name, req->name, vol->name_len + 1);
+ vol->exclusive = 1;
+ vol->ubi = ubi;
+ ubi->volumes[vol_id] = vol;
+ spin_unlock(&ubi->volumes_lock);
+
+ /*
+ * Finish all pending erases because there may be some LEBs belonging
+ * to the same volume ID.
+ */
+ err = ubi_wl_flush(ubi);
+ if (err)
+ goto out_acc;
+
+ vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL);
+ if (!vol->eba_tbl) {
+ err = -ENOMEM;
+ goto out_acc;
+ }
+
+ for (i = 0; i < vol->reserved_pebs; i++)
+ vol->eba_tbl[i] = UBI_LEB_UNMAPPED;
+
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+ vol->used_ebs = vol->reserved_pebs;
+ vol->last_eb_bytes = vol->usable_leb_size;
+ vol->used_bytes = vol->used_ebs * vol->usable_leb_size;
+ } else {
+ bytes = vol->used_bytes;
+ vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size);
+ vol->used_ebs = bytes;
+ if (vol->last_eb_bytes)
+ vol->used_ebs += 1;
+ else
+ vol->last_eb_bytes = vol->usable_leb_size;
+ }
+
+ /* Register character device for the volume */
+ cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
+ vol->cdev.owner = THIS_MODULE;
+ err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol_id + 1), 1);
+ if (err) {
+ ubi_err("cannot add character device for volume %d", vol_id);
+ goto out_mapping;
+ }
+
+ err = ubi_create_gluebi(ubi, vol);
+ if (err)
+ goto out_cdev;
+
+ vol->dev.release = vol_release;
+ vol->dev.parent = &ubi->dev;
+ vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1);
+ vol->dev.class = ubi_class;
+ sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
+ err = device_register(&vol->dev);
+ if (err)
+ goto out_gluebi;
+
+ err = volume_sysfs_init(ubi, vol);
+ if (err)
+ goto out_sysfs;
+
+ /* Fill volume table record */
+ memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record));
+ vtbl_rec.reserved_pebs = cpu_to_ubi32(vol->reserved_pebs);
+ vtbl_rec.alignment = cpu_to_ubi32(vol->alignment);
+ vtbl_rec.data_pad = cpu_to_ubi32(vol->data_pad);
+ vtbl_rec.name_len = cpu_to_ubi16(vol->name_len);
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME)
+ vtbl_rec.vol_type = UBI_VID_DYNAMIC;
+ else
+ vtbl_rec.vol_type = UBI_VID_STATIC;
+ memcpy(vtbl_rec.name, vol->name, vol->name_len + 1);
+
+ err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+ if (err)
+ goto out_sysfs;
+
+ spin_lock(&ubi->volumes_lock);
+ ubi->vol_count += 1;
+ vol->exclusive = 0;
+ spin_unlock(&ubi->volumes_lock);
+
+ paranoid_check_volumes(ubi);
+ return 0;
+
+out_gluebi:
+ err = ubi_destroy_gluebi(vol);
+out_cdev:
+ cdev_del(&vol->cdev);
+out_mapping:
+ kfree(vol->eba_tbl);
+out_acc:
+ spin_lock(&ubi->volumes_lock);
+ ubi->rsvd_pebs -= vol->reserved_pebs;
+ ubi->avail_pebs += vol->reserved_pebs;
+out_unlock:
+ spin_unlock(&ubi->volumes_lock);
+ kfree(vol);
+ return err;
+
+ /*
+ * We are registered, so @vol is destroyed in the release function and
+ * we have to de-initialize differently.
+ */
+out_sysfs:
+ err = ubi_destroy_gluebi(vol);
+ cdev_del(&vol->cdev);
+ kfree(vol->eba_tbl);
+ spin_lock(&ubi->volumes_lock);
+ ubi->rsvd_pebs -= vol->reserved_pebs;
+ ubi->avail_pebs += vol->reserved_pebs;
+ spin_unlock(&ubi->volumes_lock);
+ volume_sysfs_close(vol);
+ return err;
+}
+
+/**
+ * ubi_remove_volume - remove volume.
+ * @desc: volume descriptor
+ *
+ * This function removes volume described by @desc. The volume has to be opened
+ * in "exclusive" mode. Returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+int ubi_remove_volume(struct ubi_volume_desc *desc)
+{
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
+
+ dbg_msg("remove UBI volume %d", vol_id);
+ ubi_assert(desc->mode == UBI_EXCLUSIVE);
+ ubi_assert(vol == ubi->volumes[vol_id]);
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ err = ubi_destroy_gluebi(vol);
+ if (err)
+ return err;
+
+ err = ubi_change_vtbl_record(ubi, vol_id, NULL);
+ if (err)
+ return err;
+
+ for (i = 0; i < vol->reserved_pebs; i++) {
+ err = ubi_eba_unmap_leb(ubi, vol_id, i);
+ if (err)
+ return err;
+ }
+
+ spin_lock(&ubi->volumes_lock);
+ vol->removed = 1;
+ ubi->volumes[vol_id] = NULL;
+ spin_unlock(&ubi->volumes_lock);
+
+ kfree(vol->eba_tbl);
+ vol->eba_tbl = NULL;
+ cdev_del(&vol->cdev);
+ volume_sysfs_close(vol);
+ kfree(desc);
+
+ spin_lock(&ubi->volumes_lock);
+ ubi->rsvd_pebs -= reserved_pebs;
+ ubi->avail_pebs += reserved_pebs;
+ i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
+ if (i > 0) {
+ i = ubi->avail_pebs >= i ? i : ubi->avail_pebs;
+ ubi->avail_pebs -= i;
+ ubi->rsvd_pebs += i;
+ ubi->beb_rsvd_pebs += i;
+ if (i > 0)
+ ubi_msg("reserve more %d PEBs", i);
+ }
+ ubi->vol_count -= 1;
+ spin_unlock(&ubi->volumes_lock);
+
+ paranoid_check_volumes(ubi);
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+/**
+ * ubi_resize_volume - re-size volume.
+ * @desc: volume descriptor
+ * @reserved_pebs: new size in physical eraseblocks
+ *
+ * This function returns zero in case of success, and a negative error code in
+ * case of failure.
+ */
+int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
+{
+ int i, err, pebs, *new_mapping;
+ struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
+ struct ubi_vtbl_record vtbl_rec;
+ int vol_id = vol->vol_id;
+
+ if (ubi->ro_mode)
+ return -EROFS;
+
+ dbg_msg("re-size volume %d to from %d to %d PEBs",
+ vol_id, vol->reserved_pebs, reserved_pebs);
+ ubi_assert(desc->mode == UBI_EXCLUSIVE);
+ ubi_assert(vol == ubi->volumes[vol_id]);
+
+ if (vol->vol_type == UBI_STATIC_VOLUME &&
+ reserved_pebs < vol->used_ebs) {
+ dbg_err("too small size %d, %d LEBs contain data",
+ reserved_pebs, vol->used_ebs);
+ return -EINVAL;
+ }
+
+ /* If the size is the same, we have nothing to do */
+ if (reserved_pebs == vol->reserved_pebs)
+ return 0;
+
+ new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL);
+ if (!new_mapping)
+ return -ENOMEM;
+
+ for (i = 0; i < reserved_pebs; i++)
+ new_mapping[i] = UBI_LEB_UNMAPPED;
+
+ /* Reserve physical eraseblocks */
+ pebs = reserved_pebs - vol->reserved_pebs;
+ if (pebs > 0) {
+ spin_lock(&ubi->volumes_lock);
+ if (pebs > ubi->avail_pebs) {
+ dbg_err("not enough PEBs: requested %d, available %d",
+ pebs, ubi->avail_pebs);
+ spin_unlock(&ubi->volumes_lock);
+ err = -ENOSPC;
+ goto out_free;
+ }
+ ubi->avail_pebs -= pebs;
+ ubi->rsvd_pebs += pebs;
+ for (i = 0; i < vol->reserved_pebs; i++)
+ new_mapping[i] = vol->eba_tbl[i];
+ kfree(vol->eba_tbl);
+ vol->eba_tbl = new_mapping;
+ spin_unlock(&ubi->volumes_lock);
+ }
+
+ /* Change volume table record */
+ memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record));
+ vtbl_rec.reserved_pebs = cpu_to_ubi32(reserved_pebs);
+ err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+ if (err)
+ goto out_acc;
+
+ if (pebs < 0) {
+ for (i = 0; i < -pebs; i++) {
+ err = ubi_eba_unmap_leb(ubi, vol_id, reserved_pebs + i);
+ if (err)
+ goto out_acc;
+ }
+ spin_lock(&ubi->volumes_lock);
+ ubi->rsvd_pebs += pebs;
+ ubi->avail_pebs -= pebs;
+ pebs = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
+ if (pebs > 0) {
+ pebs = ubi->avail_pebs >= pebs ? pebs : ubi->avail_pebs;
+ ubi->avail_pebs -= pebs;
+ ubi->rsvd_pebs += pebs;
+ ubi->beb_rsvd_pebs += pebs;
+ if (pebs > 0)
+ ubi_msg("reserve more %d PEBs", pebs);
+ }
+ for (i = 0; i < reserved_pebs; i++)
+ new_mapping[i] = vol->eba_tbl[i];
+ kfree(vol->eba_tbl);
+ vol->eba_tbl = new_mapping;
+ spin_unlock(&ubi->volumes_lock);
+ }
+
+ vol->reserved_pebs = reserved_pebs;
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+ vol->used_ebs = reserved_pebs;
+ vol->last_eb_bytes = vol->usable_leb_size;
+ vol->used_bytes = vol->used_ebs * vol->usable_leb_size;
+ }
+
+ paranoid_check_volumes(ubi);
+ return 0;
+
+out_acc:
+ if (pebs > 0) {
+ spin_lock(&ubi->volumes_lock);
+ ubi->rsvd_pebs -= pebs;
+ ubi->avail_pebs += pebs;
+ spin_unlock(&ubi->volumes_lock);
+ }
+out_free:
+ kfree(new_mapping);
+ return err;
+}
+
+/**
+ * ubi_add_volume - add volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function adds an existin volume and initializes all its data
+ * structures. Returnes zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_add_volume(struct ubi_device *ubi, int vol_id)
+{
+ int err;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+
+ dbg_msg("add volume %d", vol_id);
+ ubi_dbg_dump_vol_info(vol);
+ ubi_assert(vol);
+
+ /* Register character device for the volume */
+ cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
+ vol->cdev.owner = THIS_MODULE;
+ err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol->vol_id + 1), 1);
+ if (err) {
+ ubi_err("cannot add character device for volume %d", vol_id);
+ return err;
+ }
+
+ err = ubi_create_gluebi(ubi, vol);
+ if (err)
+ goto out_cdev;
+
+ vol->dev.release = vol_release;
+ vol->dev.parent = &ubi->dev;
+ vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1);
+ vol->dev.class = ubi_class;
+ sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id);
+ err = device_register(&vol->dev);
+ if (err)
+ goto out_gluebi;
+
+ err = volume_sysfs_init(ubi, vol);
+ if (err) {
+ cdev_del(&vol->cdev);
+ err = ubi_destroy_gluebi(vol);
+ volume_sysfs_close(vol);
+ return err;
+ }
+
+ paranoid_check_volumes(ubi);
+ return 0;
+
+out_gluebi:
+ err = ubi_destroy_gluebi(vol);
+out_cdev:
+ cdev_del(&vol->cdev);
+ return err;
+}
+
+/**
+ * ubi_free_volume - free volume.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ *
+ * This function frees all resources for volume @vol_id but does not remove it.
+ * Used only when the UBI device is detached.
+ */
+void ubi_free_volume(struct ubi_device *ubi, int vol_id)
+{
+ int err;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+
+ dbg_msg("free volume %d", vol_id);
+ ubi_assert(vol);
+
+ vol->removed = 1;
+ err = ubi_destroy_gluebi(vol);
+ ubi->volumes[vol_id] = NULL;
+ cdev_del(&vol->cdev);
+ volume_sysfs_close(vol);
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_volume - check volume information.
+ * @ubi: UBI device description object
+ * @vol_id: volume ID
+ */
+static void paranoid_check_volume(const struct ubi_device *ubi, int vol_id)
+{
+ int idx = vol_id2idx(ubi, vol_id);
+ int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker;
+ const struct ubi_volume *vol = ubi->volumes[idx];
+ long long n;
+ const char *name;
+
+ reserved_pebs = ubi32_to_cpu(ubi->vtbl[vol_id].reserved_pebs);
+
+ if (!vol) {
+ if (reserved_pebs) {
+ ubi_err("no volume info, but volume exists");
+ goto fail;
+ }
+ return;
+ }
+
+ if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 ||
+ vol->name_len < 0) {
+ ubi_err("negative values");
+ goto fail;
+ }
+ if (vol->alignment > ubi->leb_size || vol->alignment == 0) {
+ ubi_err("bad alignment");
+ goto fail;
+ }
+
+ n = vol->alignment % ubi->min_io_size;
+ if (vol->alignment != 1 && n) {
+ ubi_err("alignment is not multiple of min I/O unit");
+ goto fail;
+ }
+
+ n = ubi->leb_size % vol->alignment;
+ if (vol->data_pad != n) {
+ ubi_err("bad data_pad, has to be %lld", n);
+ goto fail;
+ }
+
+ if (vol->vol_type != UBI_DYNAMIC_VOLUME &&
+ vol->vol_type != UBI_STATIC_VOLUME) {
+ ubi_err("bad vol_type");
+ goto fail;
+ }
+
+ if (vol->upd_marker != 0 && vol->upd_marker != 1) {
+ ubi_err("bad upd_marker");
+ goto fail;
+ }
+
+ if (vol->upd_marker && vol->corrupted) {
+ dbg_err("update marker and corrupted simultaneously");
+ goto fail;
+ }
+
+ if (vol->reserved_pebs > ubi->good_peb_count) {
+ ubi_err("too large reserved_pebs");
+ goto fail;
+ }
+
+ n = ubi->leb_size - vol->data_pad;
+ if (vol->usable_leb_size != ubi->leb_size - vol->data_pad) {
+ ubi_err("bad usable_leb_size, has to be %lld", n);
+ goto fail;
+ }
+
+ if (vol->name_len > UBI_VOL_NAME_MAX) {
+ ubi_err("too long volume name, max is %d", UBI_VOL_NAME_MAX);
+ goto fail;
+ }
+
+ if (!vol->name) {
+ ubi_err("NULL volume name");
+ goto fail;
+ }
+
+ n = strnlen(vol->name, vol->name_len + 1);
+ if (n != vol->name_len) {
+ ubi_err("bad name_len %lld", n);
+ goto fail;
+ }
+
+ n = vol->used_ebs * vol->usable_leb_size;
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+ if (vol->corrupted != 0) {
+ ubi_err("corrupted dynamic volume");
+ goto fail;
+ }
+ if (vol->used_ebs != vol->reserved_pebs) {
+ ubi_err("bad used_ebs");
+ goto fail;
+ }
+ if (vol->last_eb_bytes != vol->usable_leb_size) {
+ ubi_err("bad last_eb_bytes");
+ goto fail;
+ }
+ if (vol->used_bytes != n) {
+ ubi_err("bad used_bytes");
+ goto fail;
+ }
+ } else {
+ if (vol->corrupted != 0 && vol->corrupted != 1) {
+ ubi_err("bad corrupted");
+ goto fail;
+ }
+ if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) {
+ ubi_err("bad used_ebs");
+ goto fail;
+ }
+ if (vol->last_eb_bytes < 0 ||
+ vol->last_eb_bytes > vol->usable_leb_size) {
+ ubi_err("bad last_eb_bytes");
+ goto fail;
+ }
+ if (vol->used_bytes < 0 || vol->used_bytes > n ||
+ vol->used_bytes < n - vol->usable_leb_size) {
+ ubi_err("bad used_bytes");
+ goto fail;
+ }
+ }
+
+ alignment = ubi32_to_cpu(ubi->vtbl[vol_id].alignment);
+ data_pad = ubi32_to_cpu(ubi->vtbl[vol_id].data_pad);
+ name_len = ubi16_to_cpu(ubi->vtbl[vol_id].name_len);
+ upd_marker = ubi->vtbl[vol_id].upd_marker;
+ name = &ubi->vtbl[vol_id].name[0];
+ if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC)
+ vol_type = UBI_DYNAMIC_VOLUME;
+ else
+ vol_type = UBI_STATIC_VOLUME;
+
+ if (alignment != vol->alignment || data_pad != vol->data_pad ||
+ upd_marker != vol->upd_marker || vol_type != vol->vol_type ||
+ name_len!= vol->name_len || strncmp(name, vol->name, name_len)) {
+ ubi_err("volume info is different");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ ubi_err("paranoid check failed");
+ ubi_dbg_dump_vol_info(vol);
+ ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
+ BUG();
+}
+
+/**
+ * paranoid_check_volumes - check information about all volumes.
+ * @ubi: UBI device description object
+ */
+static void paranoid_check_volumes(struct ubi_device *ubi)
+{
+ int i;
+
+ mutex_lock(&ubi->vtbl_mutex);
+ spin_lock(&ubi->volumes_lock);
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ paranoid_check_volume(ubi, i);
+ spin_unlock(&ubi->volumes_lock);
+ mutex_unlock(&ubi->vtbl_mutex);
+}
+#endif
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
new file mode 100644
index 000000000000..b6fd6bbd941e
--- /dev/null
+++ b/drivers/mtd/ubi/vtbl.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2006, 2007
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * This file includes volume table manipulation code. The volume table is an
+ * on-flash table containing volume meta-data like name, number of reserved
+ * physical eraseblocks, type, etc. The volume table is stored in the so-called
+ * "layout volume".
+ *
+ * The layout volume is an internal volume which is organized as follows. It
+ * consists of two logical eraseblocks - LEB 0 and LEB 1. Each logical
+ * eraseblock stores one volume table copy, i.e. LEB 0 and LEB 1 duplicate each
+ * other. This redundancy guarantees robustness to unclean reboots. The volume
+ * table is basically an array of volume table records. Each record contains
+ * full information about the volume and protected by a CRC checksum.
+ *
+ * The volume table is changed, it is first changed in RAM. Then LEB 0 is
+ * erased, and the updated volume table is written back to LEB 0. Then same for
+ * LEB 1. This scheme guarantees recoverability from unclean reboots.
+ *
+ * In this UBI implementation the on-flash volume table does not contain any
+ * information about how many data static volumes contain. This information may
+ * be found from the scanning data.
+ *
+ * But it would still be beneficial to store this information in the volume
+ * table. For example, suppose we have a static volume X, and all its physical
+ * eraseblocks became bad for some reasons. Suppose we are attaching the
+ * corresponding MTD device, the scanning has found no logical eraseblocks
+ * corresponding to the volume X. According to the volume table volume X does
+ * exist. So we don't know whether it is just empty or all its physical
+ * eraseblocks went bad. So we cannot alarm the user about this corruption.
+ *
+ * The volume table also stores so-called "update marker", which is used for
+ * volume updates. Before updating the volume, the update marker is set, and
+ * after the update operation is finished, the update marker is cleared. So if
+ * the update operation was interrupted (e.g. by an unclean reboot) - the
+ * update marker is still there and we know that the volume's contents is
+ * damaged.
+ */
+
+#include <linux/crc32.h>
+#include <linux/err.h>
+#include <asm/div64.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static void paranoid_vtbl_check(const struct ubi_device *ubi);
+#else
+#define paranoid_vtbl_check(ubi)
+#endif
+
+/* Empty volume table record */
+static struct ubi_vtbl_record empty_vtbl_record;
+
+/**
+ * ubi_change_vtbl_record - change volume table record.
+ * @ubi: UBI device description object
+ * @idx: table index to change
+ * @vtbl_rec: new volume table record
+ *
+ * This function changes volume table record @idx. If @vtbl_rec is %NULL, empty
+ * volume table record is written. The caller does not have to calculate CRC of
+ * the record as it is done by this function. Returns zero in case of success
+ * and a negative error code in case of failure.
+ */
+int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
+ struct ubi_vtbl_record *vtbl_rec)
+{
+ int i, err;
+ uint32_t crc;
+
+ ubi_assert(idx >= 0 && idx < ubi->vtbl_slots);
+
+ if (!vtbl_rec)
+ vtbl_rec = &empty_vtbl_record;
+ else {
+ crc = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC);
+ vtbl_rec->crc = cpu_to_ubi32(crc);
+ }
+
+ dbg_msg("change record %d", idx);
+ ubi_dbg_dump_vtbl_record(vtbl_rec, idx);
+
+ mutex_lock(&ubi->vtbl_mutex);
+ memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record));
+ for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
+ err = ubi_eba_unmap_leb(ubi, UBI_LAYOUT_VOL_ID, i);
+ if (err) {
+ mutex_unlock(&ubi->vtbl_mutex);
+ return err;
+ }
+ err = ubi_eba_write_leb(ubi, UBI_LAYOUT_VOL_ID, i, ubi->vtbl, 0,
+ ubi->vtbl_size, UBI_LONGTERM);
+ if (err) {
+ mutex_unlock(&ubi->vtbl_mutex);
+ return err;
+ }
+ }
+
+ paranoid_vtbl_check(ubi);
+ mutex_unlock(&ubi->vtbl_mutex);
+ return ubi_wl_flush(ubi);
+}
+
+/**
+ * vol_til_check - check if volume table is not corrupted and contains sensible
+ * data.
+ *
+ * @ubi: UBI device description object
+ * @vtbl: volume table
+ *
+ * This function returns zero if @vtbl is all right, %1 if CRC is incorrect,
+ * and %-EINVAL if it contains inconsistent data.
+ */
+static int vtbl_check(const struct ubi_device *ubi,
+ const struct ubi_vtbl_record *vtbl)
+{
+ int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len;
+ int upd_marker;
+ uint32_t crc;
+ const char *name;
+
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ cond_resched();
+
+ reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs);
+ alignment = ubi32_to_cpu(vtbl[i].alignment);
+ data_pad = ubi32_to_cpu(vtbl[i].data_pad);
+ upd_marker = vtbl[i].upd_marker;
+ vol_type = vtbl[i].vol_type;
+ name_len = ubi16_to_cpu(vtbl[i].name_len);
+ name = &vtbl[i].name[0];
+
+ crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC);
+ if (ubi32_to_cpu(vtbl[i].crc) != crc) {
+ ubi_err("bad CRC at record %u: %#08x, not %#08x",
+ i, crc, ubi32_to_cpu(vtbl[i].crc));
+ ubi_dbg_dump_vtbl_record(&vtbl[i], i);
+ return 1;
+ }
+
+ if (reserved_pebs == 0) {
+ if (memcmp(&vtbl[i], &empty_vtbl_record,
+ UBI_VTBL_RECORD_SIZE)) {
+ dbg_err("bad empty record");
+ goto bad;
+ }
+ continue;
+ }
+
+ if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 ||
+ name_len < 0) {
+ dbg_err("negative values");
+ goto bad;
+ }
+
+ if (alignment > ubi->leb_size || alignment == 0) {
+ dbg_err("bad alignment");
+ goto bad;
+ }
+
+ n = alignment % ubi->min_io_size;
+ if (alignment != 1 && n) {
+ dbg_err("alignment is not multiple of min I/O unit");
+ goto bad;
+ }
+
+ n = ubi->leb_size % alignment;
+ if (data_pad != n) {
+ dbg_err("bad data_pad, has to be %d", n);
+ goto bad;
+ }
+
+ if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) {
+ dbg_err("bad vol_type");
+ goto bad;
+ }
+
+ if (upd_marker != 0 && upd_marker != 1) {
+ dbg_err("bad upd_marker");
+ goto bad;
+ }
+
+ if (reserved_pebs > ubi->good_peb_count) {
+ dbg_err("too large reserved_pebs, good PEBs %d",
+ ubi->good_peb_count);
+ goto bad;
+ }
+
+ if (name_len > UBI_VOL_NAME_MAX) {
+ dbg_err("too long volume name, max %d",
+ UBI_VOL_NAME_MAX);
+ goto bad;
+ }
+
+ if (name[0] == '\0') {
+ dbg_err("NULL volume name");
+ goto bad;
+ }
+
+ if (name_len != strnlen(name, name_len + 1)) {
+ dbg_err("bad name_len");
+ goto bad;
+ }
+ }
+
+ /* Checks that all names are unique */
+ for (i = 0; i < ubi->vtbl_slots - 1; i++) {
+ for (n = i + 1; n < ubi->vtbl_slots; n++) {
+ int len1 = ubi16_to_cpu(vtbl[i].name_len);
+ int len2 = ubi16_to_cpu(vtbl[n].name_len);
+
+ if (len1 > 0 && len1 == len2 &&
+ !strncmp(vtbl[i].name, vtbl[n].name, len1)) {
+ ubi_err("volumes %d and %d have the same name"
+ " \"%s\"", i, n, vtbl[i].name);
+ ubi_dbg_dump_vtbl_record(&vtbl[i], i);
+ ubi_dbg_dump_vtbl_record(&vtbl[n], n);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+
+bad:
+ ubi_err("volume table check failed, record %d", i);
+ ubi_dbg_dump_vtbl_record(&vtbl[i], i);
+ return -EINVAL;
+}
+
+/**
+ * create_vtbl - create a copy of volume table.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @copy: number of the volume table copy
+ * @vtbl: contents of the volume table
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int create_vtbl(const struct ubi_device *ubi, struct ubi_scan_info *si,
+ int copy, void *vtbl)
+{
+ int err, tries = 0;
+ static struct ubi_vid_hdr *vid_hdr;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *new_seb, *old_seb = NULL;
+
+ ubi_msg("create volume table (copy #%d)", copy + 1);
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ /*
+ * Check if there is a logical eraseblock which would have to contain
+ * this volume table copy was found during scanning. It has to be wiped
+ * out.
+ */
+ sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID);
+ if (sv)
+ old_seb = ubi_scan_find_seb(sv, copy);
+
+retry:
+ new_seb = ubi_scan_get_free_peb(ubi, si);
+ if (IS_ERR(new_seb)) {
+ err = PTR_ERR(new_seb);
+ goto out_free;
+ }
+
+ vid_hdr->vol_type = UBI_VID_DYNAMIC;
+ vid_hdr->vol_id = cpu_to_ubi32(UBI_LAYOUT_VOL_ID);
+ vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT;
+ vid_hdr->data_size = vid_hdr->used_ebs =
+ vid_hdr->data_pad = cpu_to_ubi32(0);
+ vid_hdr->lnum = cpu_to_ubi32(copy);
+ vid_hdr->sqnum = cpu_to_ubi64(++si->max_sqnum);
+ vid_hdr->leb_ver = cpu_to_ubi32(old_seb ? old_seb->leb_ver + 1: 0);
+
+ /* The EC header is already there, write the VID header */
+ err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr);
+ if (err)
+ goto write_error;
+
+ /* Write the layout volume contents */
+ err = ubi_io_write_data(ubi, vtbl, new_seb->pnum, 0, ubi->vtbl_size);
+ if (err)
+ goto write_error;
+
+ /*
+ * And add it to the scanning information. Don't delete the old
+ * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'.
+ */
+ err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec,
+ vid_hdr, 0);
+ kfree(new_seb);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+
+write_error:
+ kfree(new_seb);
+ /* May be this physical eraseblock went bad, try to pick another one */
+ if (++tries <= 5) {
+ err = ubi_scan_add_to_list(si, new_seb->pnum, new_seb->ec,
+ &si->corr);
+ if (!err)
+ goto retry;
+ }
+out_free:
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return err;
+
+}
+
+/**
+ * process_lvol - process the layout volume.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @sv: layout volume scanning information
+ *
+ * This function is responsible for reading the layout volume, ensuring it is
+ * not corrupted, and recovering from corruptions if needed. Returns volume
+ * table in case of success and a negative error code in case of failure.
+ */
+static struct ubi_vtbl_record *process_lvol(const struct ubi_device *ubi,
+ struct ubi_scan_info *si,
+ struct ubi_scan_volume *sv)
+{
+ int err;
+ struct rb_node *rb;
+ struct ubi_scan_leb *seb;
+ struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL };
+ int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1};
+
+ /*
+ * UBI goes through the following steps when it changes the layout
+ * volume:
+ * a. erase LEB 0;
+ * b. write new data to LEB 0;
+ * c. erase LEB 1;
+ * d. write new data to LEB 1.
+ *
+ * Before the change, both LEBs contain the same data.
+ *
+ * Due to unclean reboots, the contents of LEB 0 may be lost, but there
+ * should LEB 1. So it is OK if LEB 0 is corrupted while LEB 1 is not.
+ * Similarly, LEB 1 may be lost, but there should be LEB 0. And
+ * finally, unclean reboots may result in a situation when neither LEB
+ * 0 nor LEB 1 are corrupted, but they are different. In this case, LEB
+ * 0 contains more recent information.
+ *
+ * So the plan is to first check LEB 0. Then
+ * a. if LEB 0 is OK, it must be containing the most resent data; then
+ * we compare it with LEB 1, and if they are different, we copy LEB
+ * 0 to LEB 1;
+ * b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1
+ * to LEB 0.
+ */
+
+ dbg_msg("check layout volume");
+
+ /* Read both LEB 0 and LEB 1 into memory */
+ ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
+ leb[seb->lnum] = kzalloc(ubi->vtbl_size, GFP_KERNEL);
+ if (!leb[seb->lnum]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,
+ ubi->vtbl_size);
+ if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
+ /* Scrub the PEB later */
+ seb->scrub = 1;
+ else if (err)
+ goto out_free;
+ }
+
+ err = -EINVAL;
+ if (leb[0]) {
+ leb_corrupted[0] = vtbl_check(ubi, leb[0]);
+ if (leb_corrupted[0] < 0)
+ goto out_free;
+ }
+
+ if (!leb_corrupted[0]) {
+ /* LEB 0 is OK */
+ if (leb[1])
+ leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size);
+ if (leb_corrupted[1]) {
+ ubi_warn("volume table copy #2 is corrupted");
+ err = create_vtbl(ubi, si, 1, leb[0]);
+ if (err)
+ goto out_free;
+ ubi_msg("volume table was restored");
+ }
+
+ /* Both LEB 1 and LEB 2 are OK and consistent */
+ kfree(leb[1]);
+ return leb[0];
+ } else {
+ /* LEB 0 is corrupted or does not exist */
+ if (leb[1]) {
+ leb_corrupted[1] = vtbl_check(ubi, leb[1]);
+ if (leb_corrupted[1] < 0)
+ goto out_free;
+ }
+ if (leb_corrupted[1]) {
+ /* Both LEB 0 and LEB 1 are corrupted */
+ ubi_err("both volume tables are corrupted");
+ goto out_free;
+ }
+
+ ubi_warn("volume table copy #1 is corrupted");
+ err = create_vtbl(ubi, si, 0, leb[1]);
+ if (err)
+ goto out_free;
+ ubi_msg("volume table was restored");
+
+ kfree(leb[0]);
+ return leb[1];
+ }
+
+out_free:
+ kfree(leb[0]);
+ kfree(leb[1]);
+ return ERR_PTR(err);
+}
+
+/**
+ * create_empty_lvol - create empty layout volume.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns volume table contents in case of success and a
+ * negative error code in case of failure.
+ */
+static struct ubi_vtbl_record *create_empty_lvol(const struct ubi_device *ubi,
+ struct ubi_scan_info *si)
+{
+ int i;
+ struct ubi_vtbl_record *vtbl;
+
+ vtbl = kzalloc(ubi->vtbl_size, GFP_KERNEL);
+ if (!vtbl)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE);
+
+ for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
+ int err;
+
+ err = create_vtbl(ubi, si, i, vtbl);
+ if (err) {
+ kfree(vtbl);
+ return ERR_PTR(err);
+ }
+ }
+
+ return vtbl;
+}
+
+/**
+ * init_volumes - initialize volume information for existing volumes.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ * @vtbl: volume table
+ *
+ * This function allocates volume description objects for existing volumes.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
+ const struct ubi_vtbl_record *vtbl)
+{
+ int i, reserved_pebs = 0;
+ struct ubi_scan_volume *sv;
+ struct ubi_volume *vol;
+
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ cond_resched();
+
+ if (ubi32_to_cpu(vtbl[i].reserved_pebs) == 0)
+ continue; /* Empty record */
+
+ vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL);
+ if (!vol)
+ return -ENOMEM;
+
+ vol->reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs);
+ vol->alignment = ubi32_to_cpu(vtbl[i].alignment);
+ vol->data_pad = ubi32_to_cpu(vtbl[i].data_pad);
+ vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ?
+ UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME;
+ vol->name_len = ubi16_to_cpu(vtbl[i].name_len);
+ vol->usable_leb_size = ubi->leb_size - vol->data_pad;
+ memcpy(vol->name, vtbl[i].name, vol->name_len);
+ vol->name[vol->name_len] = '\0';
+ vol->vol_id = i;
+
+ ubi_assert(!ubi->volumes[i]);
+ ubi->volumes[i] = vol;
+ ubi->vol_count += 1;
+ vol->ubi = ubi;
+ reserved_pebs += vol->reserved_pebs;
+
+ /*
+ * In case of dynamic volume UBI knows nothing about how many
+ * data is stored there. So assume the whole volume is used.
+ */
+ if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
+ vol->used_ebs = vol->reserved_pebs;
+ vol->last_eb_bytes = vol->usable_leb_size;
+ vol->used_bytes = vol->used_ebs * vol->usable_leb_size;
+ continue;
+ }
+
+ /* Static volumes only */
+ sv = ubi_scan_find_sv(si, i);
+ if (!sv) {
+ /*
+ * No eraseblocks belonging to this volume found. We
+ * don't actually know whether this static volume is
+ * completely corrupted or just contains no data. And
+ * we cannot know this as long as data size is not
+ * stored on flash. So we just assume the volume is
+ * empty. FIXME: this should be handled.
+ */
+ continue;
+ }
+
+ if (sv->leb_count != sv->used_ebs) {
+ /*
+ * We found a static volume which misses several
+ * eraseblocks. Treat it as corrupted.
+ */
+ ubi_warn("static volume %d misses %d LEBs - corrupted",
+ sv->vol_id, sv->used_ebs - sv->leb_count);
+ vol->corrupted = 1;
+ continue;
+ }
+
+ vol->used_ebs = sv->used_ebs;
+ vol->used_bytes = (vol->used_ebs - 1) * vol->usable_leb_size;
+ vol->used_bytes += sv->last_data_size;
+ vol->last_eb_bytes = sv->last_data_size;
+ }
+
+ vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL);
+ if (!vol)
+ return -ENOMEM;
+
+ vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS;
+ vol->alignment = 1;
+ vol->vol_type = UBI_DYNAMIC_VOLUME;
+ vol->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1;
+ memcpy(vol->name, UBI_LAYOUT_VOLUME_NAME, vol->name_len + 1);
+ vol->usable_leb_size = ubi->leb_size;
+ vol->used_ebs = vol->reserved_pebs;
+ vol->last_eb_bytes = vol->reserved_pebs;
+ vol->used_bytes = vol->used_ebs * (ubi->leb_size - vol->data_pad);
+ vol->vol_id = UBI_LAYOUT_VOL_ID;
+
+ ubi_assert(!ubi->volumes[i]);
+ ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol;
+ reserved_pebs += vol->reserved_pebs;
+ ubi->vol_count += 1;
+ vol->ubi = ubi;
+
+ if (reserved_pebs > ubi->avail_pebs)
+ ubi_err("not enough PEBs, required %d, available %d",
+ reserved_pebs, ubi->avail_pebs);
+ ubi->rsvd_pebs += reserved_pebs;
+ ubi->avail_pebs -= reserved_pebs;
+
+ return 0;
+}
+
+/**
+ * check_sv - check volume scanning information.
+ * @vol: UBI volume description object
+ * @sv: volume scanning information
+ *
+ * This function returns zero if the volume scanning information is consistent
+ * to the data read from the volume tabla, and %-EINVAL if not.
+ */
+static int check_sv(const struct ubi_volume *vol,
+ const struct ubi_scan_volume *sv)
+{
+ if (sv->highest_lnum >= vol->reserved_pebs) {
+ dbg_err("bad highest_lnum");
+ goto bad;
+ }
+ if (sv->leb_count > vol->reserved_pebs) {
+ dbg_err("bad leb_count");
+ goto bad;
+ }
+ if (sv->vol_type != vol->vol_type) {
+ dbg_err("bad vol_type");
+ goto bad;
+ }
+ if (sv->used_ebs > vol->reserved_pebs) {
+ dbg_err("bad used_ebs");
+ goto bad;
+ }
+ if (sv->data_pad != vol->data_pad) {
+ dbg_err("bad data_pad");
+ goto bad;
+ }
+ return 0;
+
+bad:
+ ubi_err("bad scanning information");
+ ubi_dbg_dump_sv(sv);
+ ubi_dbg_dump_vol_info(vol);
+ return -EINVAL;
+}
+
+/**
+ * check_scanning_info - check that scanning information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * Even though we protect on-flash data by CRC checksums, we still don't trust
+ * the media. This function ensures that scanning information is consistent to
+ * the information read from the volume table. Returns zero if the scanning
+ * information is OK and %-EINVAL if it is not.
+ */
+static int check_scanning_info(const struct ubi_device *ubi,
+ struct ubi_scan_info *si)
+{
+ int err, i;
+ struct ubi_scan_volume *sv;
+ struct ubi_volume *vol;
+
+ if (si->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) {
+ ubi_err("scanning found %d volumes, maximum is %d + %d",
+ si->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots);
+ return -EINVAL;
+ }
+
+ if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT&&
+ si->highest_vol_id < UBI_INTERNAL_VOL_START) {
+ ubi_err("too large volume ID %d found by scanning",
+ si->highest_vol_id);
+ return -EINVAL;
+ }
+
+
+ for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
+ cond_resched();
+
+ sv = ubi_scan_find_sv(si, i);
+ vol = ubi->volumes[i];
+ if (!vol) {
+ if (sv)
+ ubi_scan_rm_volume(si, sv);
+ continue;
+ }
+
+ if (vol->reserved_pebs == 0) {
+ ubi_assert(i < ubi->vtbl_slots);
+
+ if (!sv)
+ continue;
+
+ /*
+ * During scanning we found a volume which does not
+ * exist according to the information in the volume
+ * table. This must have happened due to an unclean
+ * reboot while the volume was being removed. Discard
+ * these eraseblocks.
+ */
+ ubi_msg("finish volume %d removal", sv->vol_id);
+ ubi_scan_rm_volume(si, sv);
+ } else if (sv) {
+ err = check_sv(vol, sv);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ubi_read_volume_table - read volume table.
+ * information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function reads volume table, checks it, recover from errors if needed,
+ * or creates it if needed. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+ int i, err;
+ struct ubi_scan_volume *sv;
+
+ empty_vtbl_record.crc = cpu_to_ubi32(0xf116c36b);
+
+ /*
+ * The number of supported volumes is limited by the eraseblock size
+ * and by the UBI_MAX_VOLUMES constant.
+ */
+ ubi->vtbl_slots = ubi->leb_size / UBI_VTBL_RECORD_SIZE;
+ if (ubi->vtbl_slots > UBI_MAX_VOLUMES)
+ ubi->vtbl_slots = UBI_MAX_VOLUMES;
+
+ ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE;
+ ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size);
+
+ sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID);
+ if (!sv) {
+ /*
+ * No logical eraseblocks belonging to the layout volume were
+ * found. This could mean that the flash is just empty. In
+ * this case we create empty layout volume.
+ *
+ * But if flash is not empty this must be a corruption or the
+ * MTD device just contains garbage.
+ */
+ if (si->is_empty) {
+ ubi->vtbl = create_empty_lvol(ubi, si);
+ if (IS_ERR(ubi->vtbl))
+ return PTR_ERR(ubi->vtbl);
+ } else {
+ ubi_err("the layout volume was not found");
+ return -EINVAL;
+ }
+ } else {
+ if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) {
+ /* This must not happen with proper UBI images */
+ dbg_err("too many LEBs (%d) in layout volume",
+ sv->leb_count);
+ return -EINVAL;
+ }
+
+ ubi->vtbl = process_lvol(ubi, si, sv);
+ if (IS_ERR(ubi->vtbl))
+ return PTR_ERR(ubi->vtbl);
+ }
+
+ ubi->avail_pebs = ubi->good_peb_count;
+
+ /*
+ * The layout volume is OK, initialize the corresponding in-RAM data
+ * structures.
+ */
+ err = init_volumes(ubi, si, ubi->vtbl);
+ if (err)
+ goto out_free;
+
+ /*
+ * Get sure that the scanning information is consistent to the
+ * information stored in the volume table.
+ */
+ err = check_scanning_info(ubi, si);
+ if (err)
+ goto out_free;
+
+ return 0;
+
+out_free:
+ kfree(ubi->vtbl);
+ for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++)
+ if (ubi->volumes[i]) {
+ kfree(ubi->volumes[i]);
+ ubi->volumes[i] = NULL;
+ }
+ return err;
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_vtbl_check - check volume table.
+ * @ubi: UBI device description object
+ */
+static void paranoid_vtbl_check(const struct ubi_device *ubi)
+{
+ if (vtbl_check(ubi, ubi->vtbl)) {
+ ubi_err("paranoid check failed");
+ BUG();
+ }
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
new file mode 100644
index 000000000000..9ecaf77eca9e
--- /dev/null
+++ b/drivers/mtd/ubi/wl.c
@@ -0,0 +1,1671 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём), Thomas Gleixner
+ */
+
+/*
+ * UBI wear-leveling unit.
+ *
+ * This unit is responsible for wear-leveling. It works in terms of physical
+ * eraseblocks and erase counters and knows nothing about logical eraseblocks,
+ * volumes, etc. From this unit's perspective all physical eraseblocks are of
+ * two types - used and free. Used physical eraseblocks are those that were
+ * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are
+ * those that were put by the 'ubi_wl_put_peb()' function.
+ *
+ * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter
+ * header. The rest of the physical eraseblock contains only 0xFF bytes.
+ *
+ * When physical eraseblocks are returned to the WL unit by means of the
+ * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is
+ * done asynchronously in context of the per-UBI device background thread,
+ * which is also managed by the WL unit.
+ *
+ * The wear-leveling is ensured by means of moving the contents of used
+ * physical eraseblocks with low erase counter to free physical eraseblocks
+ * with high erase counter.
+ *
+ * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick
+ * an "optimal" physical eraseblock. For example, when it is known that the
+ * physical eraseblock will be "put" soon because it contains short-term data,
+ * the WL unit may pick a free physical eraseblock with low erase counter, and
+ * so forth.
+ *
+ * If the WL unit fails to erase a physical eraseblock, it marks it as bad.
+ *
+ * This unit is also responsible for scrubbing. If a bit-flip is detected in a
+ * physical eraseblock, it has to be moved. Technically this is the same as
+ * moving it for wear-leveling reasons.
+ *
+ * As it was said, for the UBI unit all physical eraseblocks are either "free"
+ * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used
+ * eraseblocks are kept in a set of different RB-trees: @wl->used,
+ * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub.
+ *
+ * Note, in this implementation, we keep a small in-RAM object for each physical
+ * eraseblock. This is surely not a scalable solution. But it appears to be good
+ * enough for moderately large flashes and it is simple. In future, one may
+ * re-work this unit and make it more scalable.
+ *
+ * At the moment this unit does not utilize the sequence number, which was
+ * introduced relatively recently. But it would be wise to do this because the
+ * sequence number of a logical eraseblock characterizes how old is it. For
+ * example, when we move a PEB with low erase counter, and we need to pick the
+ * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we
+ * pick target PEB with an average EC if our PEB is not very "old". This is a
+ * room for future re-works of the WL unit.
+ *
+ * FIXME: looks too complex, should be simplified (later).
+ */
+
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include "ubi.h"
+
+/* Number of physical eraseblocks reserved for wear-leveling purposes */
+#define WL_RESERVED_PEBS 1
+
+/*
+ * How many erase cycles are short term, unknown, and long term physical
+ * eraseblocks protected.
+ */
+#define ST_PROTECTION 16
+#define U_PROTECTION 10
+#define LT_PROTECTION 4
+
+/*
+ * Maximum difference between two erase counters. If this threshold is
+ * exceeded, the WL unit starts moving data from used physical eraseblocks with
+ * low erase counter to free physical eraseblocks with high erase counter.
+ */
+#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD
+
+/*
+ * When a physical eraseblock is moved, the WL unit has to pick the target
+ * physical eraseblock to move to. The simplest way would be just to pick the
+ * one with the highest erase counter. But in certain workloads this could lead
+ * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a
+ * situation when the picked physical eraseblock is constantly erased after the
+ * data is written to it. So, we have a constant which limits the highest erase
+ * counter of the free physical eraseblock to pick. Namely, the WL unit does
+ * not pick eraseblocks with erase counter greater then the lowest erase
+ * counter plus %WL_FREE_MAX_DIFF.
+ */
+#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD)
+
+/*
+ * Maximum number of consecutive background thread failures which is enough to
+ * switch to read-only mode.
+ */
+#define WL_MAX_FAILURES 32
+
+/**
+ * struct ubi_wl_entry - wear-leveling entry.
+ * @rb: link in the corresponding RB-tree
+ * @ec: erase counter
+ * @pnum: physical eraseblock number
+ *
+ * Each physical eraseblock has a corresponding &struct wl_entry object which
+ * may be kept in different RB-trees.
+ */
+struct ubi_wl_entry {
+ struct rb_node rb;
+ int ec;
+ int pnum;
+};
+
+/**
+ * struct ubi_wl_prot_entry - PEB protection entry.
+ * @rb_pnum: link in the @wl->prot.pnum RB-tree
+ * @rb_aec: link in the @wl->prot.aec RB-tree
+ * @abs_ec: the absolute erase counter value when the protection ends
+ * @e: the wear-leveling entry of the physical eraseblock under protection
+ *
+ * When the WL unit returns a physical eraseblock, the physical eraseblock is
+ * protected from being moved for some "time". For this reason, the physical
+ * eraseblock is not directly moved from the @wl->free tree to the @wl->used
+ * tree. There is one more tree in between where this physical eraseblock is
+ * temporarily stored (@wl->prot).
+ *
+ * All this protection stuff is needed because:
+ * o we don't want to move physical eraseblocks just after we have given them
+ * to the user; instead, we first want to let users fill them up with data;
+ *
+ * o there is a chance that the user will put the physical eraseblock very
+ * soon, so it makes sense not to move it for some time, but wait; this is
+ * especially important in case of "short term" physical eraseblocks.
+ *
+ * Physical eraseblocks stay protected only for limited time. But the "time" is
+ * measured in erase cycles in this case. This is implemented with help of the
+ * absolute erase counter (@wl->abs_ec). When it reaches certain value, the
+ * physical eraseblocks are moved from the protection trees (@wl->prot.*) to
+ * the @wl->used tree.
+ *
+ * Protected physical eraseblocks are searched by physical eraseblock number
+ * (when they are put) and by the absolute erase counter (to check if it is
+ * time to move them to the @wl->used tree). So there are actually 2 RB-trees
+ * storing the protected physical eraseblocks: @wl->prot.pnum and
+ * @wl->prot.aec. They are referred to as the "protection" trees. The
+ * first one is indexed by the physical eraseblock number. The second one is
+ * indexed by the absolute erase counter. Both trees store
+ * &struct ubi_wl_prot_entry objects.
+ *
+ * Each physical eraseblock has 2 main states: free and used. The former state
+ * corresponds to the @wl->free tree. The latter state is split up on several
+ * sub-states:
+ * o the WL movement is allowed (@wl->used tree);
+ * o the WL movement is temporarily prohibited (@wl->prot.pnum and
+ * @wl->prot.aec trees);
+ * o scrubbing is needed (@wl->scrub tree).
+ *
+ * Depending on the sub-state, wear-leveling entries of the used physical
+ * eraseblocks may be kept in one of those trees.
+ */
+struct ubi_wl_prot_entry {
+ struct rb_node rb_pnum;
+ struct rb_node rb_aec;
+ unsigned long long abs_ec;
+ struct ubi_wl_entry *e;
+};
+
+/**
+ * struct ubi_work - UBI work description data structure.
+ * @list: a link in the list of pending works
+ * @func: worker function
+ * @priv: private data of the worker function
+ *
+ * @e: physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * The @func pointer points to the worker function. If the @cancel argument is
+ * not zero, the worker has to free the resources and exit immediately. The
+ * worker has to return zero in case of success and a negative error code in
+ * case of failure.
+ */
+struct ubi_work {
+ struct list_head list;
+ int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel);
+ /* The below fields are only relevant to erasure works */
+ struct ubi_wl_entry *e;
+ int torture;
+};
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec);
+static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+ struct rb_root *root);
+#else
+#define paranoid_check_ec(ubi, pnum, ec) 0
+#define paranoid_check_in_wl_tree(e, root)
+#endif
+
+/* Slab cache for wear-leveling entries */
+static struct kmem_cache *wl_entries_slab;
+
+/**
+ * tree_empty - a helper function to check if an RB-tree is empty.
+ * @root: the root of the tree
+ *
+ * This function returns non-zero if the RB-tree is empty and zero if not.
+ */
+static inline int tree_empty(struct rb_root *root)
+{
+ return root->rb_node == NULL;
+}
+
+/**
+ * wl_tree_add - add a wear-leveling entry to a WL RB-tree.
+ * @e: the wear-leveling entry to add
+ * @root: the root of the tree
+ *
+ * Note, we use (erase counter, physical eraseblock number) pairs as keys in
+ * the @ubi->used and @ubi->free RB-trees.
+ */
+static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root)
+{
+ struct rb_node **p, *parent = NULL;
+
+ p = &root->rb_node;
+ while (*p) {
+ struct ubi_wl_entry *e1;
+
+ parent = *p;
+ e1 = rb_entry(parent, struct ubi_wl_entry, rb);
+
+ if (e->ec < e1->ec)
+ p = &(*p)->rb_left;
+ else if (e->ec > e1->ec)
+ p = &(*p)->rb_right;
+ else {
+ ubi_assert(e->pnum != e1->pnum);
+ if (e->pnum < e1->pnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ }
+
+ rb_link_node(&e->rb, parent, p);
+ rb_insert_color(&e->rb, root);
+}
+
+
+/*
+ * Helper functions to add and delete wear-leveling entries from different
+ * trees.
+ */
+
+static void free_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e)
+{
+ wl_tree_add(e, &ubi->free);
+}
+static inline void used_tree_add(struct ubi_device *ubi,
+ struct ubi_wl_entry *e)
+{
+ wl_tree_add(e, &ubi->used);
+}
+static inline void scrub_tree_add(struct ubi_device *ubi,
+ struct ubi_wl_entry *e)
+{
+ wl_tree_add(e, &ubi->scrub);
+}
+static inline void free_tree_del(struct ubi_device *ubi,
+ struct ubi_wl_entry *e)
+{
+ paranoid_check_in_wl_tree(e, &ubi->free);
+ rb_erase(&e->rb, &ubi->free);
+}
+static inline void used_tree_del(struct ubi_device *ubi,
+ struct ubi_wl_entry *e)
+{
+ paranoid_check_in_wl_tree(e, &ubi->used);
+ rb_erase(&e->rb, &ubi->used);
+}
+static inline void scrub_tree_del(struct ubi_device *ubi,
+ struct ubi_wl_entry *e)
+{
+ paranoid_check_in_wl_tree(e, &ubi->scrub);
+ rb_erase(&e->rb, &ubi->scrub);
+}
+
+/**
+ * do_work - do one pending work.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int do_work(struct ubi_device *ubi)
+{
+ int err;
+ struct ubi_work *wrk;
+
+ spin_lock(&ubi->wl_lock);
+
+ if (list_empty(&ubi->works)) {
+ spin_unlock(&ubi->wl_lock);
+ return 0;
+ }
+
+ wrk = list_entry(ubi->works.next, struct ubi_work, list);
+ list_del(&wrk->list);
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * Call the worker function. Do not touch the work structure
+ * after this call as it will have been freed or reused by that
+ * time by the worker function.
+ */
+ err = wrk->func(ubi, wrk, 0);
+ if (err)
+ ubi_err("work failed with error code %d", err);
+
+ spin_lock(&ubi->wl_lock);
+ ubi->works_count -= 1;
+ ubi_assert(ubi->works_count >= 0);
+ spin_unlock(&ubi->wl_lock);
+ return err;
+}
+
+/**
+ * produce_free_peb - produce a free physical eraseblock.
+ * @ubi: UBI device description object
+ *
+ * This function tries to make a free PEB by means of synchronous execution of
+ * pending works. This may be needed if, for example the background thread is
+ * disabled. Returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+static int produce_free_peb(struct ubi_device *ubi)
+{
+ int err;
+
+ spin_lock(&ubi->wl_lock);
+ while (tree_empty(&ubi->free)) {
+ spin_unlock(&ubi->wl_lock);
+
+ dbg_wl("do one work synchronously");
+ err = do_work(ubi);
+ if (err)
+ return err;
+
+ spin_lock(&ubi->wl_lock);
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ return 0;
+}
+
+/**
+ * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree.
+ * @e: the wear-leveling entry to check
+ * @root: the root of the tree
+ *
+ * This function returns non-zero if @e is in the @root RB-tree and zero if it
+ * is not.
+ */
+static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root)
+{
+ struct rb_node *p;
+
+ p = root->rb_node;
+ while (p) {
+ struct ubi_wl_entry *e1;
+
+ e1 = rb_entry(p, struct ubi_wl_entry, rb);
+
+ if (e->pnum == e1->pnum) {
+ ubi_assert(e == e1);
+ return 1;
+ }
+
+ if (e->ec < e1->ec)
+ p = p->rb_left;
+ else if (e->ec > e1->ec)
+ p = p->rb_right;
+ else {
+ ubi_assert(e->pnum != e1->pnum);
+ if (e->pnum < e1->pnum)
+ p = p->rb_left;
+ else
+ p = p->rb_right;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * prot_tree_add - add physical eraseblock to protection trees.
+ * @ubi: UBI device description object
+ * @e: the physical eraseblock to add
+ * @pe: protection entry object to use
+ * @abs_ec: absolute erase counter value when this physical eraseblock has
+ * to be removed from the protection trees.
+ *
+ * @wl->lock has to be locked.
+ */
+static void prot_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e,
+ struct ubi_wl_prot_entry *pe, int abs_ec)
+{
+ struct rb_node **p, *parent = NULL;
+ struct ubi_wl_prot_entry *pe1;
+
+ pe->e = e;
+ pe->abs_ec = ubi->abs_ec + abs_ec;
+
+ p = &ubi->prot.pnum.rb_node;
+ while (*p) {
+ parent = *p;
+ pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_pnum);
+
+ if (e->pnum < pe1->e->pnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&pe->rb_pnum, parent, p);
+ rb_insert_color(&pe->rb_pnum, &ubi->prot.pnum);
+
+ p = &ubi->prot.aec.rb_node;
+ parent = NULL;
+ while (*p) {
+ parent = *p;
+ pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_aec);
+
+ if (pe->abs_ec < pe1->abs_ec)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&pe->rb_aec, parent, p);
+ rb_insert_color(&pe->rb_aec, &ubi->prot.aec);
+}
+
+/**
+ * find_wl_entry - find wear-leveling entry closest to certain erase counter.
+ * @root: the RB-tree where to look for
+ * @max: highest possible erase counter
+ *
+ * This function looks for a wear leveling entry with erase counter closest to
+ * @max and less then @max.
+ */
+static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max)
+{
+ struct rb_node *p;
+ struct ubi_wl_entry *e;
+
+ e = rb_entry(rb_first(root), struct ubi_wl_entry, rb);
+ max += e->ec;
+
+ p = root->rb_node;
+ while (p) {
+ struct ubi_wl_entry *e1;
+
+ e1 = rb_entry(p, struct ubi_wl_entry, rb);
+ if (e1->ec >= max)
+ p = p->rb_left;
+ else {
+ p = p->rb_right;
+ e = e1;
+ }
+ }
+
+ return e;
+}
+
+/**
+ * ubi_wl_get_peb - get a physical eraseblock.
+ * @ubi: UBI device description object
+ * @dtype: type of data which will be stored in this physical eraseblock
+ *
+ * This function returns a physical eraseblock in case of success and a
+ * negative error code in case of failure. Might sleep.
+ */
+int ubi_wl_get_peb(struct ubi_device *ubi, int dtype)
+{
+ int err, protect, medium_ec;
+ struct ubi_wl_entry *e, *first, *last;
+ struct ubi_wl_prot_entry *pe;
+
+ ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM ||
+ dtype == UBI_UNKNOWN);
+
+ pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_KERNEL);
+ if (!pe)
+ return -ENOMEM;
+
+retry:
+ spin_lock(&ubi->wl_lock);
+ if (tree_empty(&ubi->free)) {
+ if (ubi->works_count == 0) {
+ ubi_assert(list_empty(&ubi->works));
+ ubi_err("no free eraseblocks");
+ spin_unlock(&ubi->wl_lock);
+ kfree(pe);
+ return -ENOSPC;
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ err = produce_free_peb(ubi);
+ if (err < 0) {
+ kfree(pe);
+ return err;
+ }
+ goto retry;
+ }
+
+ switch (dtype) {
+ case UBI_LONGTERM:
+ /*
+ * For long term data we pick a physical eraseblock
+ * with high erase counter. But the highest erase
+ * counter we can pick is bounded by the the lowest
+ * erase counter plus %WL_FREE_MAX_DIFF.
+ */
+ e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+ protect = LT_PROTECTION;
+ break;
+ case UBI_UNKNOWN:
+ /*
+ * For unknown data we pick a physical eraseblock with
+ * medium erase counter. But we by no means can pick a
+ * physical eraseblock with erase counter greater or
+ * equivalent than the lowest erase counter plus
+ * %WL_FREE_MAX_DIFF.
+ */
+ first = rb_entry(rb_first(&ubi->free),
+ struct ubi_wl_entry, rb);
+ last = rb_entry(rb_last(&ubi->free),
+ struct ubi_wl_entry, rb);
+
+ if (last->ec - first->ec < WL_FREE_MAX_DIFF)
+ e = rb_entry(ubi->free.rb_node,
+ struct ubi_wl_entry, rb);
+ else {
+ medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2;
+ e = find_wl_entry(&ubi->free, medium_ec);
+ }
+ protect = U_PROTECTION;
+ break;
+ case UBI_SHORTTERM:
+ /*
+ * For short term data we pick a physical eraseblock
+ * with the lowest erase counter as we expect it will
+ * be erased soon.
+ */
+ e = rb_entry(rb_first(&ubi->free),
+ struct ubi_wl_entry, rb);
+ protect = ST_PROTECTION;
+ break;
+ default:
+ protect = 0;
+ e = NULL;
+ BUG();
+ }
+
+ /*
+ * Move the physical eraseblock to the protection trees where it will
+ * be protected from being moved for some time.
+ */
+ free_tree_del(ubi, e);
+ prot_tree_add(ubi, e, pe, protect);
+
+ dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect);
+ spin_unlock(&ubi->wl_lock);
+
+ return e->pnum;
+}
+
+/**
+ * prot_tree_del - remove a physical eraseblock from the protection trees
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to remove
+ */
+static void prot_tree_del(struct ubi_device *ubi, int pnum)
+{
+ struct rb_node *p;
+ struct ubi_wl_prot_entry *pe = NULL;
+
+ p = ubi->prot.pnum.rb_node;
+ while (p) {
+
+ pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum);
+
+ if (pnum == pe->e->pnum)
+ break;
+
+ if (pnum < pe->e->pnum)
+ p = p->rb_left;
+ else
+ p = p->rb_right;
+ }
+
+ ubi_assert(pe->e->pnum == pnum);
+ rb_erase(&pe->rb_aec, &ubi->prot.aec);
+ rb_erase(&pe->rb_pnum, &ubi->prot.pnum);
+ kfree(pe);
+}
+
+/**
+ * sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @e: the the physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture)
+{
+ int err;
+ struct ubi_ec_hdr *ec_hdr;
+ unsigned long long ec = e->ec;
+
+ dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec);
+
+ err = paranoid_check_ec(ubi, e->pnum, e->ec);
+ if (err > 0)
+ return -EINVAL;
+
+ ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!ec_hdr)
+ return -ENOMEM;
+
+ err = ubi_io_sync_erase(ubi, e->pnum, torture);
+ if (err < 0)
+ goto out_free;
+
+ ec += err;
+ if (ec > UBI_MAX_ERASECOUNTER) {
+ /*
+ * Erase counter overflow. Upgrade UBI and use 64-bit
+ * erase counters internally.
+ */
+ ubi_err("erase counter overflow at PEB %d, EC %llu",
+ e->pnum, ec);
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ dbg_wl("erased PEB %d, new EC %llu", e->pnum, ec);
+
+ ec_hdr->ec = cpu_to_ubi64(ec);
+
+ err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr);
+ if (err)
+ goto out_free;
+
+ e->ec = ec;
+ spin_lock(&ubi->wl_lock);
+ if (e->ec > ubi->max_ec)
+ ubi->max_ec = e->ec;
+ spin_unlock(&ubi->wl_lock);
+
+out_free:
+ kfree(ec_hdr);
+ return err;
+}
+
+/**
+ * check_protection_over - check if it is time to stop protecting some
+ * physical eraseblocks.
+ * @ubi: UBI device description object
+ *
+ * This function is called after each erase operation, when the absolute erase
+ * counter is incremented, to check if some physical eraseblock have not to be
+ * protected any longer. These physical eraseblocks are moved from the
+ * protection trees to the used tree.
+ */
+static void check_protection_over(struct ubi_device *ubi)
+{
+ struct ubi_wl_prot_entry *pe;
+
+ /*
+ * There may be several protected physical eraseblock to remove,
+ * process them all.
+ */
+ while (1) {
+ spin_lock(&ubi->wl_lock);
+ if (tree_empty(&ubi->prot.aec)) {
+ spin_unlock(&ubi->wl_lock);
+ break;
+ }
+
+ pe = rb_entry(rb_first(&ubi->prot.aec),
+ struct ubi_wl_prot_entry, rb_aec);
+
+ if (pe->abs_ec > ubi->abs_ec) {
+ spin_unlock(&ubi->wl_lock);
+ break;
+ }
+
+ dbg_wl("PEB %d protection over, abs_ec %llu, PEB abs_ec %llu",
+ pe->e->pnum, ubi->abs_ec, pe->abs_ec);
+ rb_erase(&pe->rb_aec, &ubi->prot.aec);
+ rb_erase(&pe->rb_pnum, &ubi->prot.pnum);
+ used_tree_add(ubi, pe->e);
+ spin_unlock(&ubi->wl_lock);
+
+ kfree(pe);
+ cond_resched();
+ }
+}
+
+/**
+ * schedule_ubi_work - schedule a work.
+ * @ubi: UBI device description object
+ * @wrk: the work to schedule
+ *
+ * This function enqueues a work defined by @wrk to the tail of the pending
+ * works list.
+ */
+static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
+{
+ spin_lock(&ubi->wl_lock);
+ list_add_tail(&wrk->list, &ubi->works);
+ ubi_assert(ubi->works_count >= 0);
+ ubi->works_count += 1;
+ if (ubi->thread_enabled)
+ wake_up_process(ubi->bgt_thread);
+ spin_unlock(&ubi->wl_lock);
+}
+
+static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
+ int cancel);
+
+/**
+ * schedule_erase - schedule an erase work.
+ * @ubi: UBI device description object
+ * @e: the WL entry of the physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * This function returns zero in case of success and a %-ENOMEM in case of
+ * failure.
+ */
+static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
+ int torture)
+{
+ struct ubi_work *wl_wrk;
+
+ dbg_wl("schedule erasure of PEB %d, EC %d, torture %d",
+ e->pnum, e->ec, torture);
+
+ wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_KERNEL);
+ if (!wl_wrk)
+ return -ENOMEM;
+
+ wl_wrk->func = &erase_worker;
+ wl_wrk->e = e;
+ wl_wrk->torture = torture;
+
+ schedule_ubi_work(ubi, wl_wrk);
+ return 0;
+}
+
+/**
+ * wear_leveling_worker - wear-leveling worker function.
+ * @ubi: UBI device description object
+ * @wrk: the work object
+ * @cancel: non-zero if the worker has to free memory and exit
+ *
+ * This function copies a more worn out physical eraseblock to a less worn out
+ * one. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
+ int cancel)
+{
+ int err, put = 0;
+ struct ubi_wl_entry *e1, *e2;
+ struct ubi_vid_hdr *vid_hdr;
+
+ kfree(wrk);
+
+ if (cancel)
+ return 0;
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ spin_lock(&ubi->wl_lock);
+
+ /*
+ * Only one WL worker at a time is supported at this implementation, so
+ * make sure a PEB is not being moved already.
+ */
+ if (ubi->move_to || tree_empty(&ubi->free) ||
+ (tree_empty(&ubi->used) && tree_empty(&ubi->scrub))) {
+ /*
+ * Only one WL worker at a time is supported at this
+ * implementation, so if a LEB is already being moved, cancel.
+ *
+ * No free physical eraseblocks? Well, we cancel wear-leveling
+ * then. It will be triggered again when a free physical
+ * eraseblock appears.
+ *
+ * No used physical eraseblocks? They must be temporarily
+ * protected from being moved. They will be moved to the
+ * @ubi->used tree later and the wear-leveling will be
+ * triggered again.
+ */
+ dbg_wl("cancel WL, a list is empty: free %d, used %d",
+ tree_empty(&ubi->free), tree_empty(&ubi->used));
+ ubi->wl_scheduled = 0;
+ spin_unlock(&ubi->wl_lock);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return 0;
+ }
+
+ if (tree_empty(&ubi->scrub)) {
+ /*
+ * Now pick the least worn-out used physical eraseblock and a
+ * highly worn-out free physical eraseblock. If the erase
+ * counters differ much enough, start wear-leveling.
+ */
+ e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb);
+ e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+
+ if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) {
+ dbg_wl("no WL needed: min used EC %d, max free EC %d",
+ e1->ec, e2->ec);
+ ubi->wl_scheduled = 0;
+ spin_unlock(&ubi->wl_lock);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return 0;
+ }
+ used_tree_del(ubi, e1);
+ dbg_wl("move PEB %d EC %d to PEB %d EC %d",
+ e1->pnum, e1->ec, e2->pnum, e2->ec);
+ } else {
+ e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb);
+ e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+ scrub_tree_del(ubi, e1);
+ dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum);
+ }
+
+ free_tree_del(ubi, e2);
+ ubi_assert(!ubi->move_from && !ubi->move_to);
+ ubi_assert(!ubi->move_to_put && !ubi->move_from_put);
+ ubi->move_from = e1;
+ ubi->move_to = e2;
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * Now we are going to copy physical eraseblock @e1->pnum to @e2->pnum.
+ * We so far do not know which logical eraseblock our physical
+ * eraseblock (@e1) belongs to. We have to read the volume identifier
+ * header first.
+ */
+
+ err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0);
+ if (err && err != UBI_IO_BITFLIPS) {
+ if (err == UBI_IO_PEB_FREE) {
+ /*
+ * We are trying to move PEB without a VID header. UBI
+ * always write VID headers shortly after the PEB was
+ * given, so we have a situation when it did not have
+ * chance to write it down because it was preempted.
+ * Just re-schedule the work, so that next time it will
+ * likely have the VID header in place.
+ */
+ dbg_wl("PEB %d has no VID header", e1->pnum);
+ err = 0;
+ } else {
+ ubi_err("error %d while reading VID header from PEB %d",
+ err, e1->pnum);
+ if (err > 0)
+ err = -EIO;
+ }
+ goto error;
+ }
+
+ err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr);
+ if (err) {
+ if (err == UBI_IO_BITFLIPS)
+ err = 0;
+ goto error;
+ }
+
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ spin_lock(&ubi->wl_lock);
+ if (!ubi->move_to_put)
+ used_tree_add(ubi, e2);
+ else
+ put = 1;
+ ubi->move_from = ubi->move_to = NULL;
+ ubi->move_from_put = ubi->move_to_put = 0;
+ ubi->wl_scheduled = 0;
+ spin_unlock(&ubi->wl_lock);
+
+ if (put) {
+ /*
+ * Well, the target PEB was put meanwhile, schedule it for
+ * erasure.
+ */
+ dbg_wl("PEB %d was put meanwhile, erase", e2->pnum);
+ err = schedule_erase(ubi, e2, 0);
+ if (err) {
+ kmem_cache_free(wl_entries_slab, e2);
+ ubi_ro_mode(ubi);
+ }
+ }
+
+ err = schedule_erase(ubi, e1, 0);
+ if (err) {
+ kmem_cache_free(wl_entries_slab, e1);
+ ubi_ro_mode(ubi);
+ }
+
+ dbg_wl("done");
+ return err;
+
+ /*
+ * Some error occurred. @e1 was not changed, so return it back. @e2
+ * might be changed, schedule it for erasure.
+ */
+error:
+ if (err)
+ dbg_wl("error %d occurred, cancel operation", err);
+ ubi_assert(err <= 0);
+
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ spin_lock(&ubi->wl_lock);
+ ubi->wl_scheduled = 0;
+ if (ubi->move_from_put)
+ put = 1;
+ else
+ used_tree_add(ubi, e1);
+ ubi->move_from = ubi->move_to = NULL;
+ ubi->move_from_put = ubi->move_to_put = 0;
+ spin_unlock(&ubi->wl_lock);
+
+ if (put) {
+ /*
+ * Well, the target PEB was put meanwhile, schedule it for
+ * erasure.
+ */
+ dbg_wl("PEB %d was put meanwhile, erase", e1->pnum);
+ err = schedule_erase(ubi, e1, 0);
+ if (err) {
+ kmem_cache_free(wl_entries_slab, e1);
+ ubi_ro_mode(ubi);
+ }
+ }
+
+ err = schedule_erase(ubi, e2, 0);
+ if (err) {
+ kmem_cache_free(wl_entries_slab, e2);
+ ubi_ro_mode(ubi);
+ }
+
+ yield();
+ return err;
+}
+
+/**
+ * ensure_wear_leveling - schedule wear-leveling if it is needed.
+ * @ubi: UBI device description object
+ *
+ * This function checks if it is time to start wear-leveling and schedules it
+ * if yes. This function returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+static int ensure_wear_leveling(struct ubi_device *ubi)
+{
+ int err = 0;
+ struct ubi_wl_entry *e1;
+ struct ubi_wl_entry *e2;
+ struct ubi_work *wrk;
+
+ spin_lock(&ubi->wl_lock);
+ if (ubi->wl_scheduled)
+ /* Wear-leveling is already in the work queue */
+ goto out_unlock;
+
+ /*
+ * If the ubi->scrub tree is not empty, scrubbing is needed, and the
+ * the WL worker has to be scheduled anyway.
+ */
+ if (tree_empty(&ubi->scrub)) {
+ if (tree_empty(&ubi->used) || tree_empty(&ubi->free))
+ /* No physical eraseblocks - no deal */
+ goto out_unlock;
+
+ /*
+ * We schedule wear-leveling only if the difference between the
+ * lowest erase counter of used physical eraseblocks and a high
+ * erase counter of free physical eraseblocks is greater then
+ * %UBI_WL_THRESHOLD.
+ */
+ e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb);
+ e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+
+ if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD))
+ goto out_unlock;
+ dbg_wl("schedule wear-leveling");
+ } else
+ dbg_wl("schedule scrubbing");
+
+ ubi->wl_scheduled = 1;
+ spin_unlock(&ubi->wl_lock);
+
+ wrk = kmalloc(sizeof(struct ubi_work), GFP_KERNEL);
+ if (!wrk) {
+ err = -ENOMEM;
+ goto out_cancel;
+ }
+
+ wrk->func = &wear_leveling_worker;
+ schedule_ubi_work(ubi, wrk);
+ return err;
+
+out_cancel:
+ spin_lock(&ubi->wl_lock);
+ ubi->wl_scheduled = 0;
+out_unlock:
+ spin_unlock(&ubi->wl_lock);
+ return err;
+}
+
+/**
+ * erase_worker - physical eraseblock erase worker function.
+ * @ubi: UBI device description object
+ * @wl_wrk: the work object
+ * @cancel: non-zero if the worker has to free memory and exit
+ *
+ * This function erases a physical eraseblock and perform torture testing if
+ * needed. It also takes care about marking the physical eraseblock bad if
+ * needed. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
+ int cancel)
+{
+ int err;
+ struct ubi_wl_entry *e = wl_wrk->e;
+ int pnum = e->pnum;
+
+ if (cancel) {
+ dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
+ kfree(wl_wrk);
+ kmem_cache_free(wl_entries_slab, e);
+ return 0;
+ }
+
+ dbg_wl("erase PEB %d EC %d", pnum, e->ec);
+
+ err = sync_erase(ubi, e, wl_wrk->torture);
+ if (!err) {
+ /* Fine, we've erased it successfully */
+ kfree(wl_wrk);
+
+ spin_lock(&ubi->wl_lock);
+ ubi->abs_ec += 1;
+ free_tree_add(ubi, e);
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * One more erase operation has happened, take care about protected
+ * physical eraseblocks.
+ */
+ check_protection_over(ubi);
+
+ /* And take care about wear-leveling */
+ err = ensure_wear_leveling(ubi);
+ return err;
+ }
+
+ kfree(wl_wrk);
+ kmem_cache_free(wl_entries_slab, e);
+
+ if (err != -EIO) {
+ /*
+ * If this is not %-EIO, we have no idea what to do. Scheduling
+ * this physical eraseblock for erasure again would cause
+ * errors again and again. Well, lets switch to RO mode.
+ */
+ ubi_ro_mode(ubi);
+ return err;
+ }
+
+ /* It is %-EIO, the PEB went bad */
+
+ if (!ubi->bad_allowed) {
+ ubi_err("bad physical eraseblock %d detected", pnum);
+ ubi_ro_mode(ubi);
+ err = -EIO;
+ } else {
+ int need;
+
+ spin_lock(&ubi->volumes_lock);
+ need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1;
+ if (need > 0) {
+ need = ubi->avail_pebs >= need ? need : ubi->avail_pebs;
+ ubi->avail_pebs -= need;
+ ubi->rsvd_pebs += need;
+ ubi->beb_rsvd_pebs += need;
+ if (need > 0)
+ ubi_msg("reserve more %d PEBs", need);
+ }
+
+ if (ubi->beb_rsvd_pebs == 0) {
+ spin_unlock(&ubi->volumes_lock);
+ ubi_err("no reserved physical eraseblocks");
+ ubi_ro_mode(ubi);
+ return -EIO;
+ }
+
+ spin_unlock(&ubi->volumes_lock);
+ ubi_msg("mark PEB %d as bad", pnum);
+
+ err = ubi_io_mark_bad(ubi, pnum);
+ if (err) {
+ ubi_ro_mode(ubi);
+ return err;
+ }
+
+ spin_lock(&ubi->volumes_lock);
+ ubi->beb_rsvd_pebs -= 1;
+ ubi->bad_peb_count += 1;
+ ubi->good_peb_count -= 1;
+ ubi_calculate_reserved(ubi);
+ if (ubi->beb_rsvd_pebs == 0)
+ ubi_warn("last PEB from the reserved pool was used");
+ spin_unlock(&ubi->volumes_lock);
+ }
+
+ return err;
+}
+
+/**
+ * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling
+ * unit.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to return
+ * @torture: if this physical eraseblock has to be tortured
+ *
+ * This function is called to return physical eraseblock @pnum to the pool of
+ * free physical eraseblocks. The @torture flag has to be set if an I/O error
+ * occurred to this @pnum and it has to be tested. This function returns zero
+ * in case of success and a negative error code in case of failure.
+ */
+int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture)
+{
+ int err;
+ struct ubi_wl_entry *e;
+
+ dbg_wl("PEB %d", pnum);
+ ubi_assert(pnum >= 0);
+ ubi_assert(pnum < ubi->peb_count);
+
+ spin_lock(&ubi->wl_lock);
+
+ e = ubi->lookuptbl[pnum];
+ if (e == ubi->move_from) {
+ /*
+ * User is putting the physical eraseblock which was selected to
+ * be moved. It will be scheduled for erasure in the
+ * wear-leveling worker.
+ */
+ dbg_wl("PEB %d is being moved", pnum);
+ ubi_assert(!ubi->move_from_put);
+ ubi->move_from_put = 1;
+ spin_unlock(&ubi->wl_lock);
+ return 0;
+ } else if (e == ubi->move_to) {
+ /*
+ * User is putting the physical eraseblock which was selected
+ * as the target the data is moved to. It may happen if the EBA
+ * unit already re-mapped the LEB but the WL unit did has not
+ * put the PEB to the "used" tree.
+ */
+ dbg_wl("PEB %d is the target of data moving", pnum);
+ ubi_assert(!ubi->move_to_put);
+ ubi->move_to_put = 1;
+ spin_unlock(&ubi->wl_lock);
+ return 0;
+ } else {
+ if (in_wl_tree(e, &ubi->used))
+ used_tree_del(ubi, e);
+ else if (in_wl_tree(e, &ubi->scrub))
+ scrub_tree_del(ubi, e);
+ else
+ prot_tree_del(ubi, e->pnum);
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ err = schedule_erase(ubi, e, torture);
+ if (err) {
+ spin_lock(&ubi->wl_lock);
+ used_tree_add(ubi, e);
+ spin_unlock(&ubi->wl_lock);
+ }
+
+ return err;
+}
+
+/**
+ * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to schedule
+ *
+ * If a bit-flip in a physical eraseblock is detected, this physical eraseblock
+ * needs scrubbing. This function schedules a physical eraseblock for
+ * scrubbing which is done in background. This function returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum)
+{
+ struct ubi_wl_entry *e;
+
+ ubi_msg("schedule PEB %d for scrubbing", pnum);
+
+retry:
+ spin_lock(&ubi->wl_lock);
+ e = ubi->lookuptbl[pnum];
+ if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub)) {
+ spin_unlock(&ubi->wl_lock);
+ return 0;
+ }
+
+ if (e == ubi->move_to) {
+ /*
+ * This physical eraseblock was used to move data to. The data
+ * was moved but the PEB was not yet inserted to the proper
+ * tree. We should just wait a little and let the WL worker
+ * proceed.
+ */
+ spin_unlock(&ubi->wl_lock);
+ dbg_wl("the PEB %d is not in proper tree, retry", pnum);
+ yield();
+ goto retry;
+ }
+
+ if (in_wl_tree(e, &ubi->used))
+ used_tree_del(ubi, e);
+ else
+ prot_tree_del(ubi, pnum);
+
+ scrub_tree_add(ubi, e);
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * Technically scrubbing is the same as wear-leveling, so it is done
+ * by the WL worker.
+ */
+ return ensure_wear_leveling(ubi);
+}
+
+/**
+ * ubi_wl_flush - flush all pending works.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_wl_flush(struct ubi_device *ubi)
+{
+ int err, pending_count;
+
+ pending_count = ubi->works_count;
+
+ dbg_wl("flush (%d pending works)", pending_count);
+
+ /*
+ * Erase while the pending works queue is not empty, but not more then
+ * the number of currently pending works.
+ */
+ while (pending_count-- > 0) {
+ err = do_work(ubi);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * tree_destroy - destroy an RB-tree.
+ * @root: the root of the tree to destroy
+ */
+static void tree_destroy(struct rb_root *root)
+{
+ struct rb_node *rb;
+ struct ubi_wl_entry *e;
+
+ rb = root->rb_node;
+ while (rb) {
+ if (rb->rb_left)
+ rb = rb->rb_left;
+ else if (rb->rb_right)
+ rb = rb->rb_right;
+ else {
+ e = rb_entry(rb, struct ubi_wl_entry, rb);
+
+ rb = rb_parent(rb);
+ if (rb) {
+ if (rb->rb_left == &e->rb)
+ rb->rb_left = NULL;
+ else
+ rb->rb_right = NULL;
+ }
+
+ kmem_cache_free(wl_entries_slab, e);
+ }
+ }
+}
+
+/**
+ * ubi_thread - UBI background thread.
+ * @u: the UBI device description object pointer
+ */
+static int ubi_thread(void *u)
+{
+ int failures = 0;
+ struct ubi_device *ubi = u;
+
+ ubi_msg("background thread \"%s\" started, PID %d",
+ ubi->bgt_name, current->pid);
+
+ for (;;) {
+ int err;
+
+ if (kthread_should_stop())
+ goto out;
+
+ if (try_to_freeze())
+ continue;
+
+ spin_lock(&ubi->wl_lock);
+ if (list_empty(&ubi->works) || ubi->ro_mode ||
+ !ubi->thread_enabled) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock(&ubi->wl_lock);
+ schedule();
+ continue;
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ err = do_work(ubi);
+ if (err) {
+ ubi_err("%s: work failed with error code %d",
+ ubi->bgt_name, err);
+ if (failures++ > WL_MAX_FAILURES) {
+ /*
+ * Too many failures, disable the thread and
+ * switch to read-only mode.
+ */
+ ubi_msg("%s: %d consecutive failures",
+ ubi->bgt_name, WL_MAX_FAILURES);
+ ubi_ro_mode(ubi);
+ break;
+ }
+ } else
+ failures = 0;
+
+ cond_resched();
+ }
+
+out:
+ dbg_wl("background thread \"%s\" is killed", ubi->bgt_name);
+ return 0;
+}
+
+/**
+ * cancel_pending - cancel all pending works.
+ * @ubi: UBI device description object
+ */
+static void cancel_pending(struct ubi_device *ubi)
+{
+ while (!list_empty(&ubi->works)) {
+ struct ubi_work *wrk;
+
+ wrk = list_entry(ubi->works.next, struct ubi_work, list);
+ list_del(&wrk->list);
+ wrk->func(ubi, wrk, 1);
+ ubi->works_count -= 1;
+ ubi_assert(ubi->works_count >= 0);
+ }
+}
+
+/**
+ * ubi_wl_init_scan - initialize the wear-leveling unit using scanning
+ * information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero in case of success, and a negative error code in
+ * case of failure.
+ */
+int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+ int err;
+ struct rb_node *rb1, *rb2;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *seb, *tmp;
+ struct ubi_wl_entry *e;
+
+
+ ubi->used = ubi->free = ubi->scrub = RB_ROOT;
+ ubi->prot.pnum = ubi->prot.aec = RB_ROOT;
+ spin_lock_init(&ubi->wl_lock);
+ ubi->max_ec = si->max_ec;
+ INIT_LIST_HEAD(&ubi->works);
+
+ sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num);
+
+ ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
+ if (IS_ERR(ubi->bgt_thread)) {
+ err = PTR_ERR(ubi->bgt_thread);
+ ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
+ err);
+ return err;
+ }
+
+ if (ubi_devices_cnt == 0) {
+ wl_entries_slab = kmem_cache_create("ubi_wl_entry_slab",
+ sizeof(struct ubi_wl_entry),
+ 0, 0, NULL, NULL);
+ if (!wl_entries_slab)
+ return -ENOMEM;
+ }
+
+ err = -ENOMEM;
+ ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL);
+ if (!ubi->lookuptbl)
+ goto out_free;
+
+ list_for_each_entry_safe(seb, tmp, &si->erase, u.list) {
+ cond_resched();
+
+ e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL);
+ if (!e)
+ goto out_free;
+
+ e->pnum = seb->pnum;
+ e->ec = seb->ec;
+ ubi->lookuptbl[e->pnum] = e;
+ if (schedule_erase(ubi, e, 0)) {
+ kmem_cache_free(wl_entries_slab, e);
+ goto out_free;
+ }
+ }
+
+ list_for_each_entry(seb, &si->free, u.list) {
+ cond_resched();
+
+ e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL);
+ if (!e)
+ goto out_free;
+
+ e->pnum = seb->pnum;
+ e->ec = seb->ec;
+ ubi_assert(e->ec >= 0);
+ free_tree_add(ubi, e);
+ ubi->lookuptbl[e->pnum] = e;
+ }
+
+ list_for_each_entry(seb, &si->corr, u.list) {
+ cond_resched();
+
+ e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL);
+ if (!e)
+ goto out_free;
+
+ e->pnum = seb->pnum;
+ e->ec = seb->ec;
+ ubi->lookuptbl[e->pnum] = e;
+ if (schedule_erase(ubi, e, 0)) {
+ kmem_cache_free(wl_entries_slab, e);
+ goto out_free;
+ }
+ }
+
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+ cond_resched();
+
+ e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL);
+ if (!e)
+ goto out_free;
+
+ e->pnum = seb->pnum;
+ e->ec = seb->ec;
+ ubi->lookuptbl[e->pnum] = e;
+ if (!seb->scrub) {
+ dbg_wl("add PEB %d EC %d to the used tree",
+ e->pnum, e->ec);
+ used_tree_add(ubi, e);
+ } else {
+ dbg_wl("add PEB %d EC %d to the scrub tree",
+ e->pnum, e->ec);
+ scrub_tree_add(ubi, e);
+ }
+ }
+ }
+
+ if (WL_RESERVED_PEBS > ubi->avail_pebs) {
+ ubi_err("no enough physical eraseblocks (%d, need %d)",
+ ubi->avail_pebs, WL_RESERVED_PEBS);
+ goto out_free;
+ }
+ ubi->avail_pebs -= WL_RESERVED_PEBS;
+ ubi->rsvd_pebs += WL_RESERVED_PEBS;
+
+ /* Schedule wear-leveling if needed */
+ err = ensure_wear_leveling(ubi);
+ if (err)
+ goto out_free;
+
+ return 0;
+
+out_free:
+ cancel_pending(ubi);
+ tree_destroy(&ubi->used);
+ tree_destroy(&ubi->free);
+ tree_destroy(&ubi->scrub);
+ kfree(ubi->lookuptbl);
+ if (ubi_devices_cnt == 0)
+ kmem_cache_destroy(wl_entries_slab);
+ return err;
+}
+
+/**
+ * protection_trees_destroy - destroy the protection RB-trees.
+ * @ubi: UBI device description object
+ */
+static void protection_trees_destroy(struct ubi_device *ubi)
+{
+ struct rb_node *rb;
+ struct ubi_wl_prot_entry *pe;
+
+ rb = ubi->prot.aec.rb_node;
+ while (rb) {
+ if (rb->rb_left)
+ rb = rb->rb_left;
+ else if (rb->rb_right)
+ rb = rb->rb_right;
+ else {
+ pe = rb_entry(rb, struct ubi_wl_prot_entry, rb_aec);
+
+ rb = rb_parent(rb);
+ if (rb) {
+ if (rb->rb_left == &pe->rb_aec)
+ rb->rb_left = NULL;
+ else
+ rb->rb_right = NULL;
+ }
+
+ kmem_cache_free(wl_entries_slab, pe->e);
+ kfree(pe);
+ }
+ }
+}
+
+/**
+ * ubi_wl_close - close the wear-leveling unit.
+ * @ubi: UBI device description object
+ */
+void ubi_wl_close(struct ubi_device *ubi)
+{
+ dbg_wl("disable \"%s\"", ubi->bgt_name);
+ if (ubi->bgt_thread)
+ kthread_stop(ubi->bgt_thread);
+
+ dbg_wl("close the UBI wear-leveling unit");
+
+ cancel_pending(ubi);
+ protection_trees_destroy(ubi);
+ tree_destroy(&ubi->used);
+ tree_destroy(&ubi->free);
+ tree_destroy(&ubi->scrub);
+ kfree(ubi->lookuptbl);
+ if (ubi_devices_cnt == 1)
+ kmem_cache_destroy(wl_entries_slab);
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_ec - make sure that the erase counter of a physical eraseblock
+ * is correct.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ * @ec: the erase counter to check
+ *
+ * This function returns zero if the erase counter of physical eraseblock @pnum
+ * is equivalent to @ec, %1 if not, and a negative error code if an error
+ * occurred.
+ */
+static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec)
+{
+ int err;
+ long long read_ec;
+ struct ubi_ec_hdr *ec_hdr;
+
+ ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
+ if (!ec_hdr)
+ return -ENOMEM;
+
+ err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0);
+ if (err && err != UBI_IO_BITFLIPS) {
+ /* The header does not have to exist */
+ err = 0;
+ goto out_free;
+ }
+
+ read_ec = ubi64_to_cpu(ec_hdr->ec);
+ if (ec != read_ec) {
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_err("read EC is %lld, should be %d", read_ec, ec);
+ ubi_dbg_dump_stack();
+ err = 1;
+ } else
+ err = 0;
+
+out_free:
+ kfree(ec_hdr);
+ return err;
+}
+
+/**
+ * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present
+ * in a WL RB-tree.
+ * @e: the wear-leveling entry to check
+ * @root: the root of the tree
+ *
+ * This function returns zero if @e is in the @root RB-tree and %1 if it
+ * is not.
+ */
+static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+ struct rb_root *root)
+{
+ if (in_wl_tree(e, root))
+ return 0;
+
+ ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ",
+ e->pnum, e->ec, root);
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index 06e33786078d..4bee99ba7dbb 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -735,7 +735,6 @@ static void el_receive(struct net_device *dev)
else
{
skb_reserve(skb,2); /* Force 16 byte alignment */
- skb->dev = dev;
/*
* The read increments through the bytes. The interrupt
* handler will fix the pointer when it returns to
diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c
index 702bfb2a5e99..e985a85a5623 100644
--- a/drivers/net/3c505.c
+++ b/drivers/net/3c505.c
@@ -615,7 +615,6 @@ static void receive_packet(struct net_device *dev, int len)
if (test_and_set_bit(0, (void *) &adapter->dmaing))
printk(KERN_ERR "%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction);
- skb->dev = dev;
adapter->current_dma.direction = 0;
adapter->current_dma.length = rlen;
adapter->current_dma.skb = skb;
@@ -1026,7 +1025,7 @@ static int send_packet(struct net_device *dev, struct sk_buff *skb)
adapter->current_dma.start_time = jiffies;
if ((unsigned long)(skb->data + nlen) >= MAX_DMA_ADDRESS || nlen != skb->len) {
- memcpy(adapter->dma_buffer, skb->data, nlen);
+ skb_copy_from_linear_data(skb, adapter->dma_buffer, nlen);
memset(adapter->dma_buffer+skb->len, 0, nlen-skb->len);
target = isa_virt_to_bus(adapter->dma_buffer);
}
diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c
index 54e1d5aebed3..eed4299dc426 100644
--- a/drivers/net/3c507.c
+++ b/drivers/net/3c507.c
@@ -873,7 +873,6 @@ static void el16_rx(struct net_device *dev)
}
skb_reserve(skb,2);
- skb->dev = dev;
/* 'skb->data' points to the start of sk_buff data area. */
memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len);
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index f791bf026e51..c7511c4d3b68 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -1091,7 +1091,6 @@ el3_rx(struct net_device *dev)
printk("Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* Align IP on 16 byte */
/* 'skb->data' points to the start of sk_buff data area. */
diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c
index c307ce66145c..290166d5e7d1 100644
--- a/drivers/net/3c515.c
+++ b/drivers/net/3c515.c
@@ -1292,7 +1292,6 @@ static int corkscrew_rx(struct net_device *dev)
printk("Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
/* 'skb_put()' points to the start of sk_buff data area. */
insl(ioaddr + RX_FIFO,
@@ -1363,7 +1362,6 @@ static int boomerang_rx(struct net_device *dev)
copying to a properly sized skbuff. */
if (pkt_len < rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 4)) != 0) {
- skb->dev = dev;
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
/* 'skb_put()' points to the start of sk_buff data area. */
memcpy(skb_put(skb, pkt_len),
diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c
index 17d61eb0a7e5..da1a22c13865 100644
--- a/drivers/net/3c523.c
+++ b/drivers/net/3c523.c
@@ -988,7 +988,6 @@ static void elmc_rcv_int(struct net_device *dev)
rbd->status = 0;
skb = (struct sk_buff *) dev_alloc_skb(totlen + 2);
if (skb != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte alignment */
skb_put(skb,totlen);
eth_copy_and_sum(skb, (char *) p->base+(unsigned long) rbd->buffer,totlen,0);
@@ -1146,7 +1145,7 @@ static int elmc_send_packet(struct sk_buff *skb, struct net_device *dev)
if (len != skb->len)
memset((char *) p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN);
- memcpy((char *) p->xmit_cbuffs[p->xmit_count], (char *) (skb->data), skb->len);
+ skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len);
#if (NUM_XMIT_BUFFS == 1)
#ifdef NO_NOPCOMMANDS
diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c
index 6c7437e60bd2..c7b571be20e0 100644
--- a/drivers/net/3c527.c
+++ b/drivers/net/3c527.c
@@ -1189,7 +1189,6 @@ static void mc32_rx_ring(struct net_device *dev)
}
skb->protocol=eth_type_trans(skb,dev);
- skb->dev=dev;
dev->last_rx = jiffies;
lp->net_stats.rx_packets++;
lp->net_stats.rx_bytes += length;
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index b406ecfa7268..80924f76dee8 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -2414,7 +2414,6 @@ static int vortex_rx(struct net_device *dev)
printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
/* 'skb_put()' points to the start of sk_buff data area. */
if (vp->bus_master &&
@@ -2491,7 +2490,6 @@ boomerang_rx(struct net_device *dev)
/* Check if the packet is long enough to just accept without
copying to a properly sized skbuff. */
if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
- skb->dev = dev;
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
pci_dma_sync_single_for_cpu(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
/* 'skb_put()' points to the start of sk_buff data area. */
diff --git a/drivers/net/7990.c b/drivers/net/7990.c
index 1b3d11ed6cff..d396f996af57 100644
--- a/drivers/net/7990.c
+++ b/drivers/net/7990.c
@@ -331,7 +331,6 @@ static int lance_rx (struct net_device *dev)
return 0;
}
- skb->dev = dev;
skb_reserve (skb, 2); /* 16 byte align */
skb_put (skb, len); /* make room */
eth_copy_and_sum(skb,
@@ -568,7 +567,7 @@ int lance_start_xmit (struct sk_buff *skb, struct net_device *dev)
if (skb->len < ETH_ZLEN)
memset((char *)&ib->tx_buf[entry][0], 0, ETH_ZLEN);
- memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen);
+ skb_copy_from_linear_data(skb, &ib->tx_buf[entry][0], skblen);
/* Now, give the packet to the lance */
ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c
index 12c8453f44bc..e8c9f27817b0 100644
--- a/drivers/net/8139cp.c
+++ b/drivers/net/8139cp.c
@@ -573,7 +573,6 @@ rx_status_loop:
}
skb_reserve(new_skb, RX_OFFSET);
- new_skb->dev = dev;
pci_unmap_single(cp->pdev, mapping,
buflen, PCI_DMA_FROMDEVICE);
@@ -807,7 +806,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev)
if (mss)
flags |= LargeSend | ((mss & MSSMask) << MSSShift);
else if (skb->ip_summed == CHECKSUM_PARTIAL) {
- const struct iphdr *ip = skb->nh.iph;
+ const struct iphdr *ip = ip_hdr(skb);
if (ip->protocol == IPPROTO_TCP)
flags |= IPCS | TCPCS;
else if (ip->protocol == IPPROTO_UDP)
@@ -826,7 +825,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev)
u32 first_len, first_eor;
dma_addr_t first_mapping;
int frag, first_entry = entry;
- const struct iphdr *ip = skb->nh.iph;
+ const struct iphdr *ip = ip_hdr(skb);
/* We must give this initial chunk to the device last.
* Otherwise we could race with the device.
@@ -1082,7 +1081,6 @@ static int cp_refill_rx (struct cp_private *cp)
if (!skb)
goto err_out;
- skb->dev = cp->dev;
skb_reserve(skb, RX_OFFSET);
mapping = pci_map_single(cp->pdev, skb->data, cp->rx_buf_sz,
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c
index 99304b2aa86e..a844b1fe2dc4 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/8139too.c
@@ -1904,10 +1904,10 @@ static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring,
u32 left = RX_BUF_LEN - offset;
if (size > left) {
- memcpy(skb->data, ring + offset, left);
- memcpy(skb->data+left, ring, size - left);
+ skb_copy_to_linear_data(skb, ring + offset, left);
+ skb_copy_to_linear_data_offset(skb, left, ring, size - left);
} else
- memcpy(skb->data, ring + offset, size);
+ skb_copy_to_linear_data(skb, ring + offset, size);
}
#endif
@@ -2013,7 +2013,6 @@ no_early_rx:
skb = dev_alloc_skb (pkt_size + 2);
if (likely(skb)) {
- skb->dev = dev;
skb_reserve (skb, 2); /* 16 byte align the IP fields. */
#if RX_BUF_IDX == 3
wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
diff --git a/drivers/net/82596.c b/drivers/net/82596.c
index 640d7ca2ebcf..3ff1155459a3 100644
--- a/drivers/net/82596.c
+++ b/drivers/net/82596.c
@@ -830,7 +830,6 @@ memory_squeeze:
lp->stats.rx_dropped++;
}
else {
- skb->dev = dev;
if (!rx_in_place) {
/* 16 byte align the data fields */
skb_reserve(skb, 2);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 753695b624b3..890c85e2b4ae 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2263,6 +2263,7 @@ config GIANFAR
tristate "Gianfar Ethernet"
depends on 85xx || 83xx || PPC_86xx
select PHYLIB
+ select CRC32
help
This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx,
and MPC86xx family of chips, and the FEC on the 8540.
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 33af833667da..58527322a39d 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -206,7 +206,7 @@ obj-$(CONFIG_TR) += tokenring/
obj-$(CONFIG_WAN) += wan/
obj-$(CONFIG_ARCNET) += arcnet/
obj-$(CONFIG_NET_PCMCIA) += pcmcia/
-obj-$(CONFIG_NET_RADIO) += wireless/
+obj-y += wireless/
obj-$(CONFIG_NET_TULIP) += tulip/
obj-$(CONFIG_HAMRADIO) += hamradio/
obj-$(CONFIG_IRDA) += irda/
diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c
index d76548e75350..1226cbba0450 100644
--- a/drivers/net/a2065.c
+++ b/drivers/net/a2065.c
@@ -320,7 +320,6 @@ static int lance_rx (struct net_device *dev)
return 0;
}
- skb->dev = dev;
skb_reserve (skb, 2); /* 16 byte align */
skb_put (skb, len); /* make room */
eth_copy_and_sum(skb,
@@ -599,7 +598,7 @@ static int lance_start_xmit (struct sk_buff *skb, struct net_device *dev)
ib->btx_ring [entry].length = (-len) | 0xf000;
ib->btx_ring [entry].misc = 0;
- memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen);
+ skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen);
/* Clear the slack of the packet, do I need this? */
if (len != skblen)
diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c
index 7138e0e025bc..7122b7ba8d61 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/acenic.c
@@ -2027,7 +2027,6 @@ static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm)
*/
csum = retdesc->tcp_udp_csum;
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
/*
diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c
index 962c954c2d56..675fe918421b 100644
--- a/drivers/net/amd8111e.c
+++ b/drivers/net/amd8111e.c
@@ -798,9 +798,7 @@ static int amd8111e_rx_poll(struct net_device *dev, int * budget)
pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index],
lp->rx_buff_len-2, PCI_DMA_FROMDEVICE);
skb_put(skb, pkt_len);
- skb->dev = dev;
lp->rx_skbuff[rx_index] = new_skb;
- new_skb->dev = dev;
lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev,
new_skb->data,
lp->rx_buff_len-2,
@@ -926,9 +924,7 @@ static int amd8111e_rx(struct net_device *dev)
pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index],
lp->rx_buff_len-2, PCI_DMA_FROMDEVICE);
skb_put(skb, pkt_len);
- skb->dev = dev;
lp->rx_skbuff[rx_index] = new_skb;
- new_skb->dev = dev;
lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev,
new_skb->data, lp->rx_buff_len-2,PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index dba5e5165452..da6ffa8cd81e 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -853,9 +853,9 @@ static void cops_rx(struct net_device *dev)
return;
}
- skb->mac.raw = skb->data; /* Point to entire packet. */
+ skb_reset_mac_header(skb); /* Point to entire packet. */
skb_pull(skb,3);
- skb->h.raw = skb->data; /* Point to data (Skip header). */
+ skb_reset_transport_header(skb); /* Point to data (Skip header). */
/* Update the counters. */
lp->stats.rx_packets++;
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index 2ea44ce49810..6a6cbd331a16 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -770,13 +770,13 @@ static int sendup_buffer (struct net_device *dev)
skb->data[0] = dnode;
skb->data[1] = snode;
skb->data[2] = llaptype;
- skb->mac.raw = skb->data; /* save pointer to llap header */
+ skb_reset_mac_header(skb); /* save pointer to llap header */
skb_pull(skb,3);
/* copy ddp(s,e)hdr + contents */
- memcpy(skb->data,(void*)ltdmabuf,len);
+ skb_copy_to_linear_data(skb, ltdmabuf, len);
- skb->h.raw = skb->data;
+ skb_reset_transport_header(skb);
stats->rx_packets++;
stats->rx_bytes+=skb->len;
@@ -917,13 +917,14 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
int i;
struct lt_sendlap cbuf;
+ unsigned char *hdr;
cbuf.command = LT_SENDLAP;
cbuf.dnode = skb->data[0];
cbuf.laptype = skb->data[2];
skb_pull(skb,3); /* skip past LLAP header */
cbuf.length = skb->len; /* this is host order */
- skb->h.raw=skb->data;
+ skb_reset_transport_header(skb);
if(debug & DEBUG_UPPER) {
printk("command ");
@@ -932,11 +933,13 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
printk("\n");
}
- do_write(dev,&cbuf,sizeof(cbuf),skb->h.raw,skb->len);
+ hdr = skb_transport_header(skb);
+ do_write(dev, &cbuf, sizeof(cbuf), hdr, skb->len);
if(debug & DEBUG_UPPER) {
printk("sent %d ddp bytes\n",skb->len);
- for(i=0;i<skb->len;i++) printk("%02x ",skb->h.raw[i]);
+ for (i = 0; i < skb->len; i++)
+ printk("%02x ", hdr[i]);
printk("\n");
}
diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c
index 6318814a11a8..e0a18e7c73cb 100644
--- a/drivers/net/arcnet/arc-rawmode.c
+++ b/drivers/net/arcnet/arc-rawmode.c
@@ -110,7 +110,7 @@ static void rx(struct net_device *dev, int bufnum,
pkt = (struct archdr *) skb->data;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, ARC_HDR_SIZE);
/* up to sizeof(pkt->soft) has already been copied from the card */
diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c
index 83004fdab0a4..681e20b8466f 100644
--- a/drivers/net/arcnet/arcnet.c
+++ b/drivers/net/arcnet/arcnet.c
@@ -519,9 +519,12 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
* real header when we do rebuild_header.
*/
*(uint16_t *) skb_push(skb, 2) = type;
- if (skb->nh.raw - skb->mac.raw != 2)
+ /*
+ * XXX: Why not use skb->mac_len?
+ */
+ if (skb->network_header - skb->mac_header != 2)
BUGMSG(D_NORMAL, "arcnet_header: Yikes! diff (%d) is not 2!\n",
- (int)(skb->nh.raw - skb->mac.raw));
+ (int)(skb->network_header - skb->mac_header));
return -2; /* return error -- can't transmit yet! */
}
else {
@@ -554,11 +557,13 @@ static int arcnet_rebuild_header(struct sk_buff *skb)
unsigned short type;
uint8_t daddr=0;
struct ArcProto *proto;
-
- if (skb->nh.raw - skb->mac.raw != 2) {
+ /*
+ * XXX: Why not use skb->mac_len?
+ */
+ if (skb->network_header - skb->mac_header != 2) {
BUGMSG(D_NORMAL,
- "rebuild_header: shouldn't be here! (hdrsize=%d)\n",
- (int)(skb->nh.raw - skb->mac.raw));
+ "rebuild_header: shouldn't be here! (hdrsize=%d)\n",
+ (int)(skb->network_header - skb->mac_header));
return 0;
}
type = *(uint16_t *) skb_pull(skb, 2);
diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c
index 66485585ab39..cc4610db6395 100644
--- a/drivers/net/arcnet/capmode.c
+++ b/drivers/net/arcnet/capmode.c
@@ -122,10 +122,8 @@ static void rx(struct net_device *dev, int bufnum,
}
skb_put(skb, length + ARC_HDR_SIZE + sizeof(int));
skb->dev = dev;
-
- pkt = (struct archdr *) skb->data;
-
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
+ pkt = (struct archdr *)skb_mac_header(skb);
skb_pull(skb, ARC_HDR_SIZE);
/* up to sizeof(pkt->soft) has already been copied from the card */
@@ -270,13 +268,13 @@ static int ack_tx(struct net_device *dev, int acked)
skb_put(ackskb, length + ARC_HDR_SIZE );
ackskb->dev = dev;
- ackpkt = (struct archdr *) ackskb->data;
-
- ackskb->mac.raw = ackskb->data;
+ skb_reset_mac_header(ackskb);
+ ackpkt = (struct archdr *)skb_mac_header(ackskb);
/* skb_pull(ackskb, ARC_HDR_SIZE); */
- memcpy(ackpkt, lp->outgoing.skb->data, ARC_HDR_SIZE+sizeof(struct arc_cap));
+ skb_copy_from_linear_data(lp->outgoing.skb, ackpkt,
+ ARC_HDR_SIZE + sizeof(struct arc_cap));
ackpkt->soft.cap.proto=0; /* using protocol 0 for acknowledge */
ackpkt->soft.cap.mes.ack=acked;
diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c
index 6d6c69f036ef..2de8877ece29 100644
--- a/drivers/net/arcnet/rfc1051.c
+++ b/drivers/net/arcnet/rfc1051.c
@@ -94,7 +94,7 @@ static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev)
int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
/* Pull off the arcnet header. */
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, hdr_size);
if (pkt->hard.dest == 0)
diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c
index bee34226abfa..460a095000c2 100644
--- a/drivers/net/arcnet/rfc1201.c
+++ b/drivers/net/arcnet/rfc1201.c
@@ -96,7 +96,7 @@ static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev)
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
/* Pull off the arcnet header. */
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, hdr_size);
if (pkt->hard.dest == 0)
diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c
index 9dfc09b181c1..a0e68e718531 100644
--- a/drivers/net/ariadne.c
+++ b/drivers/net/ariadne.c
@@ -743,7 +743,6 @@ static int ariadne_rx(struct net_device *dev)
}
- skb->dev = dev;
skb_reserve(skb,2); /* 16 byte align */
skb_put(skb,pkt_len); /* Make room */
eth_copy_and_sum(skb, (char *)priv->rx_buff[entry], pkt_len,0);
diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c
index ddd12d44ff22..8f0d7ce503c9 100644
--- a/drivers/net/arm/am79c961a.c
+++ b/drivers/net/arm/am79c961a.c
@@ -526,7 +526,6 @@ am79c961_rx(struct net_device *dev, struct dev_priv *priv)
skb = dev_alloc_skb(len + 2);
if (skb) {
- skb->dev = dev;
skb_reserve(skb, 2);
am_readbuffer(dev, pktaddr, skb_put(skb, len), len);
diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c
index 1621b8fe35cf..152fa7a042b8 100644
--- a/drivers/net/arm/at91_ether.c
+++ b/drivers/net/arm/at91_ether.c
@@ -858,7 +858,6 @@ static void at91ether_rx(struct net_device *dev)
skb_reserve(skb, 2);
memcpy(skb_put(skb, pktlen), p_recv, pktlen);
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
dev->last_rx = jiffies;
lp->stats.rx_bytes += pktlen;
diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c
index dd698b033a62..2438c5bff237 100644
--- a/drivers/net/arm/ep93xx_eth.c
+++ b/drivers/net/arm/ep93xx_eth.c
@@ -255,7 +255,6 @@ static int ep93xx_rx(struct net_device *dev, int *budget)
skb = dev_alloc_skb(length + 2);
if (likely(skb != NULL)) {
- skb->dev = dev;
skb_reserve(skb, 2);
dma_sync_single(NULL, ep->descs->rdesc[entry].buf_addr,
length, DMA_FROM_DEVICE);
diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c
index a2921882eba8..f075cebe84ad 100644
--- a/drivers/net/arm/ether1.c
+++ b/drivers/net/arm/ether1.c
@@ -875,7 +875,6 @@ ether1_recv_done (struct net_device *dev)
skb = dev_alloc_skb (length + 2);
if (skb) {
- skb->dev = dev;
skb_reserve (skb, 2);
ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length);
diff --git a/drivers/net/arm/ether3.c b/drivers/net/arm/ether3.c
index 841178343a07..32da2eb9bcee 100644
--- a/drivers/net/arm/ether3.c
+++ b/drivers/net/arm/ether3.c
@@ -661,7 +661,6 @@ if (next_ptr < RX_START || next_ptr >= RX_END) {
if (skb) {
unsigned char *buf;
- skb->dev = dev;
skb_reserve(skb, 2);
buf = skb_put(skb, length);
ether3_readbuffer(dev, buf + 12, length - 12);
diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c
index 56ae8babd919..bed8e0ebaf19 100644
--- a/drivers/net/at1700.c
+++ b/drivers/net/at1700.c
@@ -768,7 +768,6 @@ net_rx(struct net_device *dev)
lp->stats.rx_dropped++;
break;
}
- skb->dev = dev;
skb_reserve(skb,2);
insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
diff --git a/drivers/net/atari_bionet.c b/drivers/net/atari_bionet.c
index 4e3bf6a1f22c..3d87bd2b4194 100644
--- a/drivers/net/atari_bionet.c
+++ b/drivers/net/atari_bionet.c
@@ -453,7 +453,8 @@ bionet_send_packet(struct sk_buff *skb, struct net_device *dev) {
stdma_lock(bionet_intr, NULL);
local_irq_restore(flags);
if( !STRAM_ADDR(buf+length-1) ) {
- memcpy(nic_packet->buffer, skb->data, length);
+ skb_copy_from_linear_data(skb, nic_packet->buffer,
+ length);
buf = (unsigned long)&((struct nic_pkt_s *)phys_nic_packet)->buffer;
}
@@ -544,13 +545,13 @@ bionet_poll_rx(struct net_device *dev) {
break;
}
- skb->dev = dev;
skb_reserve( skb, 2 ); /* 16 Byte align */
skb_put( skb, pkt_len ); /* make room */
/* 'skb->data' points to the start of sk_buff data area.
*/
- memcpy(skb->data, nic_packet->buffer, pkt_len);
+ skb_copy_to_linear_data(skb, nic_packet->buffer,
+ pkt_len);
skb->protocol = eth_type_trans( skb, dev );
netif_rx(skb);
dev->last_rx = jiffies;
diff --git a/drivers/net/atari_pamsnet.c b/drivers/net/atari_pamsnet.c
index 3b5436149286..54714409a09b 100644
--- a/drivers/net/atari_pamsnet.c
+++ b/drivers/net/atari_pamsnet.c
@@ -717,7 +717,8 @@ pamsnet_send_packet(struct sk_buff *skb, struct net_device *dev) {
local_irq_restore(flags);
if( !STRAM_ADDR(buf+length-1) ) {
- memcpy(nic_packet->buffer, skb->data, length);
+ skb_copy_from_linear_data(skb, nic_packet->buffer,
+ length);
buf = (unsigned long)phys_nic_packet;
}
@@ -792,7 +793,8 @@ pamsnet_poll_rx(struct net_device *dev) {
/* 'skb->data' points to the start of sk_buff data area.
*/
- memcpy(skb->data, nic_packet->buffer, pkt_len);
+ skb_copy_to_linear_data(skb, nic_packet->buffer,
+ pkt_len);
netif_rx(skb);
dev->last_rx = jiffies;
lp->stats.rx_packets++;
diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c
index 7e37ac86a69a..dfa8b9ba4c80 100644
--- a/drivers/net/atarilance.c
+++ b/drivers/net/atarilance.c
@@ -1047,7 +1047,6 @@ static int lance_rx( struct net_device *dev )
pkt_len );
}
- skb->dev = dev;
skb_reserve( skb, 2 ); /* 16 byte align */
skb_put( skb, pkt_len ); /* Make room */
lp->memcpy_f( skb->data, PKTBUF_ADDR(head), pkt_len );
diff --git a/drivers/net/atl1/atl1_main.c b/drivers/net/atl1/atl1_main.c
index 8606eac5bec8..4b1d4d153ecf 100644
--- a/drivers/net/atl1/atl1_main.c
+++ b/drivers/net/atl1/atl1_main.c
@@ -408,7 +408,6 @@ static void atl1_rx_checksum(struct atl1_adapter *adapter,
static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter)
{
struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
- struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
struct page *page;
unsigned long offset;
@@ -444,7 +443,6 @@ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter)
* the 14 byte MAC header is removed
*/
skb_reserve(skb, NET_IP_ALIGN);
- skb->dev = netdev;
buffer_info->alloced = 1;
buffer_info->skb = skb;
@@ -1296,19 +1294,21 @@ static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb,
}
if (skb->protocol == ntohs(ETH_P_IP)) {
- skb->nh.iph->tot_len = 0;
- skb->nh.iph->check = 0;
- skb->h.th->check =
- ~csum_tcpudp_magic(skb->nh.iph->saddr,
- skb->nh.iph->daddr, 0,
- IPPROTO_TCP, 0);
- ipofst = skb->nh.raw - skb->data;
+ struct iphdr *iph = ip_hdr(skb);
+
+ iph->tot_len = 0;
+ iph->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0,
+ IPPROTO_TCP,
+ 0);
+ ipofst = skb_network_offset(skb);
if (ipofst != ENET_HEADER_SIZE) /* 802.3 frame */
tso->tsopl |= 1 << TSO_PARAM_ETHTYPE_SHIFT;
- tso->tsopl |= (skb->nh.iph->ihl &
+ tso->tsopl |= (iph->ihl &
CSUM_PARAM_IPHL_MASK) << CSUM_PARAM_IPHL_SHIFT;
- tso->tsopl |= ((skb->h.th->doff << 2) &
+ tso->tsopl |= (tcp_hdrlen(skb) &
TSO_PARAM_TCPHDRLEN_MASK) << TSO_PARAM_TCPHDRLEN_SHIFT;
tso->tsopl |= (skb_shinfo(skb)->gso_size &
TSO_PARAM_MSS_MASK) << TSO_PARAM_MSS_SHIFT;
@@ -1327,8 +1327,8 @@ static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb,
u8 css, cso;
if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
- cso = skb->h.raw - skb->data;
- css = (skb->h.raw + skb->csum_offset) - skb->data;
+ cso = skb_transport_offset(skb);
+ css = cso + skb->csum_offset;
if (unlikely(cso & 0x1)) {
printk(KERN_DEBUG "%s: payload offset != even number\n",
atl1_driver_name);
@@ -1370,8 +1370,7 @@ static void atl1_tx_map(struct atl1_adapter *adapter,
if (tcp_seg) {
/* TSO/GSO */
- proto_hdr_len =
- ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+ proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
buffer_info->length = proto_hdr_len;
page = virt_to_page(skb->data);
offset = (unsigned long)skb->data & ~PAGE_MASK;
@@ -1563,8 +1562,8 @@ static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
mss = skb_shinfo(skb)->gso_size;
if (mss) {
if (skb->protocol == htons(ETH_P_IP)) {
- proto_hdr_len = ((skb->h.raw - skb->data) +
- (skb->h.th->doff << 2));
+ proto_hdr_len = (skb_transport_offset(skb) +
+ tcp_hdrlen(skb));
if (unlikely(proto_hdr_len > len)) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
diff --git a/drivers/net/atp.c b/drivers/net/atp.c
index 2d306fcb7f36..18aba838c1ff 100644
--- a/drivers/net/atp.c
+++ b/drivers/net/atp.c
@@ -793,7 +793,6 @@ static void net_rx(struct net_device *dev)
lp->stats.rx_dropped++;
goto done;
}
- skb->dev = dev;
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c
index 69ae229b680e..d10fb80e9a63 100644
--- a/drivers/net/au1000_eth.c
+++ b/drivers/net/au1000_eth.c
@@ -1125,7 +1125,7 @@ static int au1000_tx(struct sk_buff *skb, struct net_device *dev)
}
pDB = aup->tx_db_inuse[aup->tx_head];
- memcpy((void *)pDB->vaddr, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, pDB->vaddr, skb->len);
if (skb->len < ETH_ZLEN) {
for (i=skb->len; i<ETH_ZLEN; i++) {
((char *)pDB->vaddr)[i] = 0;
@@ -1205,7 +1205,6 @@ static int au1000_rx(struct net_device *dev)
aup->stats.rx_dropped++;
continue;
}
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte IP header align */
eth_copy_and_sum(skb,
(unsigned char *)pDB->vaddr, frmlen, 0);
diff --git a/drivers/net/b44.c b/drivers/net/b44.c
index d742bfe24471..879a2fff474e 100644
--- a/drivers/net/b44.c
+++ b/drivers/net/b44.c
@@ -825,12 +825,11 @@ static int b44_rx(struct b44 *bp, int budget)
if (copy_skb == NULL)
goto drop_it_no_recycle;
- copy_skb->dev = bp->dev;
skb_reserve(copy_skb, 2);
skb_put(copy_skb, len);
/* DMA sync done above, copy just the actual packet */
- memcpy(copy_skb->data, skb->data+bp->rx_offset, len);
-
+ skb_copy_from_linear_data_offset(skb, bp->rx_offset,
+ copy_skb->data, len);
skb = copy_skb;
}
skb->ip_summed = CHECKSUM_NONE;
@@ -1007,7 +1006,8 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto err_out;
}
- memcpy(skb_put(bounce_skb, len), skb->data, skb->len);
+ skb_copy_from_linear_data(skb, skb_put(bounce_skb, len),
+ skb->len);
dev_kfree_skb_any(skb);
skb = bounce_skb;
}
diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c
index c143304dcff5..4612725965df 100644
--- a/drivers/net/bmac.c
+++ b/drivers/net/bmac.c
@@ -715,7 +715,6 @@ static irqreturn_t bmac_rxdma_intr(int irq, void *dev_id)
if (skb != NULL) {
nb -= ETHERCRC;
skb_put(skb, nb);
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->last_rx = jiffies;
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 0b7aded8dcfd..f98a2205a090 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -54,8 +54,8 @@
#define DRV_MODULE_NAME "bnx2"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "1.5.7"
-#define DRV_MODULE_RELDATE "March 29, 2007"
+#define DRV_MODULE_VERSION "1.5.8"
+#define DRV_MODULE_RELDATE "April 24, 2007"
#define RUN_AT(x) (jiffies + (x))
@@ -1884,10 +1884,8 @@ bnx2_rx_int(struct bnx2 *bp, int budget)
goto reuse_rx;
/* aligned copy */
- memcpy(new_skb->data,
- skb->data + bp->rx_offset - 2,
- len + 2);
-
+ skb_copy_from_linear_data_offset(skb, bp->rx_offset - 2,
+ new_skb->data, len + 2);
skb_reserve(new_skb, 2);
skb_put(new_skb, len);
@@ -3421,6 +3419,9 @@ bnx2_init_chip(struct bnx2 *bp)
val = REG_RD(bp, BNX2_MQ_CONFIG);
val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE;
val |= BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256;
+ if (CHIP_ID(bp) == CHIP_ID_5709_A0 || CHIP_ID(bp) == CHIP_ID_5709_A1)
+ val |= BNX2_MQ_CONFIG_HALT_DIS;
+
REG_WR(bp, BNX2_MQ_CONFIG, val);
val = 0x10000 + (MAX_CID_CNT * MB_KERNEL_CTX_SIZE);
@@ -4510,6 +4511,7 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
if ((mss = skb_shinfo(skb)->gso_size) &&
(skb->len > (bp->dev->mtu + ETH_HLEN))) {
u32 tcp_opt_len, ip_tcp_len;
+ struct iphdr *iph;
if (skb_header_cloned(skb) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
@@ -4517,25 +4519,23 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
- tcp_opt_len = ((skb->h.th->doff - 5) * 4);
vlan_tag_flags |= TX_BD_FLAGS_SW_LSO;
tcp_opt_len = 0;
- if (skb->h.th->doff > 5) {
- tcp_opt_len = (skb->h.th->doff - 5) << 2;
- }
- ip_tcp_len = (skb->nh.iph->ihl << 2) + sizeof(struct tcphdr);
-
- skb->nh.iph->check = 0;
- skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
- skb->h.th->check =
- ~csum_tcpudp_magic(skb->nh.iph->saddr,
- skb->nh.iph->daddr,
- 0, IPPROTO_TCP, 0);
-
- if (tcp_opt_len || (skb->nh.iph->ihl > 5)) {
- vlan_tag_flags |= ((skb->nh.iph->ihl - 5) +
- (tcp_opt_len >> 2)) << 8;
+ if (tcp_hdr(skb)->doff > 5)
+ tcp_opt_len = tcp_optlen(skb);
+
+ ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr);
+
+ iph = ip_hdr(skb);
+ iph->check = 0;
+ iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0,
+ IPPROTO_TCP, 0);
+ if (tcp_opt_len || (iph->ihl > 5)) {
+ vlan_tag_flags |= ((iph->ihl - 5) +
+ (tcp_opt_len >> 2)) << 8;
}
}
else
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index ccbdf81c6599..878eee58f12a 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -6518,6 +6518,7 @@ struct bnx2 {
#define CHIP_ID_5708_B0 0x57081000
#define CHIP_ID_5708_B1 0x57081010
#define CHIP_ID_5709_A0 0x57090000
+#define CHIP_ID_5709_A1 0x57090010
#define CHIP_BOND_ID(bp) (((bp)->chip_id) & 0xf)
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 3fb354d9c515..7e03f41ae2c2 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -884,8 +884,8 @@ static int ad_lacpdu_send(struct port *port)
}
skb->dev = slave->dev;
- skb->mac.raw = skb->data;
- skb->nh.raw = skb->data + ETH_HLEN;
+ skb_reset_mac_header(skb);
+ skb->network_header = skb->mac_header + ETH_HLEN;
skb->protocol = PKT_TYPE_LACPDU;
skb->priority = TC_PRIO_CONTROL;
@@ -928,8 +928,8 @@ static int ad_marker_send(struct port *port, struct marker *marker)
skb_reserve(skb, 16);
skb->dev = slave->dev;
- skb->mac.raw = skb->data;
- skb->nh.raw = skb->data + ETH_HLEN;
+ skb_reset_mac_header(skb);
+ skb->network_header = skb->mac_header + ETH_HLEN;
skb->protocol = PKT_TYPE_LACPDU;
marker_header = (struct marker_header *)skb_put(skb, length);
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 217a2eedee0a..92c3b6f6a8e7 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -104,10 +104,15 @@ struct arp_pkt {
};
#pragma pack()
+static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb)
+{
+ return (struct arp_pkt *)skb_network_header(skb);
+}
+
/* Forward declaration */
static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]);
-static inline u8 _simple_hash(u8 *hash_start, int hash_size)
+static inline u8 _simple_hash(const u8 *hash_start, int hash_size)
{
int i;
u8 hash = 0;
@@ -613,7 +618,7 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, u32 src_ip)
static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bond)
{
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
- struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw;
+ struct arp_pkt *arp = arp_pkt(skb);
struct slave *assigned_slave;
struct rlb_client_info *client_info;
u32 hash_index = 0;
@@ -701,7 +706,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
*/
static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
{
- struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw;
+ struct arp_pkt *arp = arp_pkt(skb);
struct slave *tx_slave = NULL;
if (arp->op_code == __constant_htons(ARPOP_REPLY)) {
@@ -890,8 +895,8 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
data = skb_put(skb, size);
memcpy(data, &pkt, size);
- skb->mac.raw = data;
- skb->nh.raw = data + ETH_HLEN;
+ skb_reset_mac_header(skb);
+ skb->network_header = skb->mac_header + ETH_HLEN;
skb->protocol = pkt.type;
skb->priority = TC_PRIO_CONTROL;
skb->dev = slave->dev;
@@ -1263,10 +1268,10 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
int hash_size = 0;
int do_tx_balance = 1;
u32 hash_index = 0;
- u8 *hash_start = NULL;
+ const u8 *hash_start = NULL;
int res = 1;
- skb->mac.raw = (unsigned char *)skb->data;
+ skb_reset_mac_header(skb);
eth_data = eth_hdr(skb);
/* make sure that the curr_active_slave and the slaves list do
@@ -1280,15 +1285,18 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
}
switch (ntohs(skb->protocol)) {
- case ETH_P_IP:
+ case ETH_P_IP: {
+ const struct iphdr *iph = ip_hdr(skb);
+
if ((memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) ||
- (skb->nh.iph->daddr == ip_bcast) ||
- (skb->nh.iph->protocol == IPPROTO_IGMP)) {
+ (iph->daddr == ip_bcast) ||
+ (iph->protocol == IPPROTO_IGMP)) {
do_tx_balance = 0;
break;
}
- hash_start = (char*)&(skb->nh.iph->daddr);
- hash_size = sizeof(skb->nh.iph->daddr);
+ hash_start = (char *)&(iph->daddr);
+ hash_size = sizeof(iph->daddr);
+ }
break;
case ETH_P_IPV6:
if (memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) {
@@ -1296,8 +1304,8 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
break;
}
- hash_start = (char*)&(skb->nh.ipv6h->daddr);
- hash_size = sizeof(skb->nh.ipv6h->daddr);
+ hash_start = (char *)&(ipv6_hdr(skb)->daddr);
+ hash_size = sizeof(ipv6_hdr(skb)->daddr);
break;
case ETH_P_IPX:
if (ipx_hdr(skb)->ipx_checksum !=
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index e4724d874e7c..cea3783c92c5 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2524,7 +2524,7 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
(2 * sizeof(u32)))))
goto out_unlock;
- arp = skb->nh.arph;
+ arp = arp_hdr(skb);
if (arp->ar_hln != dev->addr_len ||
skb->pkt_type == PACKET_OTHERHOST ||
skb->pkt_type == PACKET_LOOPBACK ||
@@ -3476,7 +3476,7 @@ static int bond_xmit_hash_policy_l34(struct sk_buff *skb,
struct net_device *bond_dev, int count)
{
struct ethhdr *data = (struct ethhdr *)skb->data;
- struct iphdr *iph = skb->nh.iph;
+ struct iphdr *iph = ip_hdr(skb);
u16 *layer4hdr = (u16 *)((u32 *)iph + iph->ihl);
int layer4_xor = 0;
@@ -3640,9 +3640,8 @@ static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
read_lock_bh(&bond->lock);
bond_for_each_slave(bond, slave, i) {
- if (slave->dev->get_stats) {
- sstats = slave->dev->get_stats(slave->dev);
-
+ sstats = slave->dev->get_stats(slave->dev);
+ if (sstats) {
stats->rx_packets += sstats->rx_packets;
stats->rx_bytes += sstats->rx_bytes;
stats->rx_errors += sstats->rx_errors;
diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c
index c8126484c2be..4aec747d9e43 100644
--- a/drivers/net/cassini.c
+++ b/drivers/net/cassini.c
@@ -1995,7 +1995,6 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc,
return -1;
*skbref = skb;
- skb->dev = cp->dev;
skb_reserve(skb, swivel);
p = skb->data;
@@ -2822,10 +2821,8 @@ static inline int cas_xmit_tx_ringN(struct cas *cp, int ring,
ctrl = 0;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- u64 csum_start_off, csum_stuff_off;
-
- csum_start_off = (u64) (skb->h.raw - skb->data);
- csum_stuff_off = csum_start_off + skb->csum_offset;
+ const u64 csum_start_off = skb_transport_offset(skb);
+ const u64 csum_stuff_off = csum_start_off + skb->csum_offset;
ctrl = TX_DESC_CSUM_EN |
CAS_BASE(TX_DESC_CSUM_START, csum_start_off) |
@@ -2849,8 +2846,8 @@ static inline int cas_xmit_tx_ringN(struct cas *cp, int ring,
ctrl | TX_DESC_SOF, 0);
entry = TX_DESC_NEXT(ring, entry);
- memcpy(tx_tiny_buf(cp, ring, entry), skb->data +
- len - tabort, tabort);
+ skb_copy_from_linear_data_offset(skb, len - tabort,
+ tx_tiny_buf(cp, ring, entry), tabort);
mapping = tx_tiny_map(cp, ring, entry, tentry);
cas_write_txd(cp, ring, entry, mapping, tabort, ctrl,
(nr_frags == 0));
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c
index 326d4a665123..e4f874a70fe5 100644
--- a/drivers/net/chelsio/sge.c
+++ b/drivers/net/chelsio/sge.c
@@ -1062,7 +1062,7 @@ static inline struct sk_buff *get_packet(struct pci_dev *pdev,
pci_unmap_addr(ce, dma_addr),
pci_unmap_len(ce, dma_len),
PCI_DMA_FROMDEVICE);
- memcpy(skb->data, ce->skb->data, len);
+ skb_copy_from_linear_data(ce->skb, skb->data, len);
pci_dma_sync_single_for_device(pdev,
pci_unmap_addr(ce, dma_addr),
pci_unmap_len(ce, dma_len),
@@ -1379,12 +1379,11 @@ static void sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len)
}
__skb_pull(skb, sizeof(*p));
- skb->dev = adapter->port[p->iff].dev;
skb->dev->last_rx = jiffies;
st = per_cpu_ptr(sge->port_stats[p->iff], smp_processor_id());
st->rx_packets++;
- skb->protocol = eth_type_trans(skb, skb->dev);
+ skb->protocol = eth_type_trans(skb, adapter->port[p->iff].dev);
if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff &&
skb->protocol == htons(ETH_P_IP) &&
(skb->data[9] == IPPROTO_TCP || skb->data[9] == IPPROTO_UDP)) {
@@ -1866,14 +1865,14 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev)
++st->tx_tso;
- eth_type = skb->nh.raw - skb->data == ETH_HLEN ?
+ eth_type = skb_network_offset(skb) == ETH_HLEN ?
CPL_ETH_II : CPL_ETH_II_VLAN;
hdr = (struct cpl_tx_pkt_lso *)skb_push(skb, sizeof(*hdr));
hdr->opcode = CPL_TX_PKT_LSO;
hdr->ip_csum_dis = hdr->l4_csum_dis = 0;
- hdr->ip_hdr_words = skb->nh.iph->ihl;
- hdr->tcp_hdr_words = skb->h.th->doff;
+ hdr->ip_hdr_words = ip_hdr(skb)->ihl;
+ hdr->tcp_hdr_words = tcp_hdr(skb)->doff;
hdr->eth_type_mss = htons(MK_ETH_TYPE_MSS(eth_type,
skb_shinfo(skb)->gso_size));
hdr->len = htonl(skb->len - sizeof(*hdr));
@@ -1913,7 +1912,7 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (!(adapter->flags & UDP_CSUM_CAPABLE) &&
skb->ip_summed == CHECKSUM_PARTIAL &&
- skb->nh.iph->protocol == IPPROTO_UDP) {
+ ip_hdr(skb)->protocol == IPPROTO_UDP) {
if (unlikely(skb_checksum_help(skb))) {
pr_debug("%s: unable to do udp checksum\n", dev->name);
dev_kfree_skb_any(skb);
@@ -1926,7 +1925,7 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev)
*/
if ((unlikely(!adapter->sge->espibug_skb[dev->if_port]))) {
if (skb->protocol == htons(ETH_P_ARP) &&
- skb->nh.arph->ar_op == htons(ARPOP_REQUEST)) {
+ arp_hdr(skb)->ar_op == htons(ARPOP_REQUEST)) {
adapter->sge->espibug_skb[dev->if_port] = skb;
/* We want to re-use this skb later. We
* simply bump the reference count and it
@@ -2096,10 +2095,14 @@ static void espibug_workaround_t204(unsigned long data)
0x0, 0x7, 0x43, 0x0, 0x0, 0x0
};
- memcpy(skb->data + sizeof(struct cpl_tx_pkt),
- ch_mac_addr, ETH_ALEN);
- memcpy(skb->data + skb->len - 10,
- ch_mac_addr, ETH_ALEN);
+ skb_copy_to_linear_data_offset(skb,
+ sizeof(struct cpl_tx_pkt),
+ ch_mac_addr,
+ ETH_ALEN);
+ skb_copy_to_linear_data_offset(skb,
+ skb->len - 10,
+ ch_mac_addr,
+ ETH_ALEN);
skb->cb[0] = 0xff;
}
@@ -2126,10 +2129,14 @@ static void espibug_workaround(unsigned long data)
if (!skb->cb[0]) {
u8 ch_mac_addr[ETH_ALEN] =
{0x0, 0x7, 0x43, 0x0, 0x0, 0x0};
- memcpy(skb->data + sizeof(struct cpl_tx_pkt),
- ch_mac_addr, ETH_ALEN);
- memcpy(skb->data + skb->len - 10, ch_mac_addr,
- ETH_ALEN);
+ skb_copy_to_linear_data_offset(skb,
+ sizeof(struct cpl_tx_pkt),
+ ch_mac_addr,
+ ETH_ALEN);
+ skb_copy_to_linear_data_offset(skb,
+ skb->len - 10,
+ ch_mac_addr,
+ ETH_ALEN);
skb->cb[0] = 0xff;
}
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
index 8eb571276000..5bdf5ca85a65 100644
--- a/drivers/net/cris/eth_v10.c
+++ b/drivers/net/cris/eth_v10.c
@@ -1348,7 +1348,8 @@ e100_rx(struct net_device *dev)
#ifdef ETHDEBUG
printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n",
- skb->head, skb->data, skb->tail, skb->end);
+ skb->head, skb->data, skb_tail_pointer(skb),
+ skb_end_pointer(skb));
printk("copying packet to 0x%x.\n", skb_data_ptr);
#endif
@@ -1375,7 +1376,6 @@ e100_rx(struct net_device *dev)
myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data));
}
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
/* Send the packet to the upper layers */
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index 4612f71a7106..9774bb1b3e80 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -1004,7 +1004,6 @@ skip_this_frame:
return;
}
skb_reserve(skb, 2); /* longword align L3 header */
- skb->dev = dev;
if (bp + length > lp->end_dma_buff) {
int semi_cnt = lp->end_dma_buff - bp;
@@ -1702,7 +1701,6 @@ net_rx(struct net_device *dev)
return;
}
skb_reserve(skb, 2); /* longword align L3 header */
- skb->dev = dev;
readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
if (length & 1)
diff --git a/drivers/net/cxgb3/cxgb3_defs.h b/drivers/net/cxgb3/cxgb3_defs.h
index e14862b43d17..483a594210a7 100644
--- a/drivers/net/cxgb3/cxgb3_defs.h
+++ b/drivers/net/cxgb3/cxgb3_defs.h
@@ -67,7 +67,10 @@ static inline union listen_entry *stid2entry(const struct tid_info *t,
static inline struct t3c_tid_entry *lookup_tid(const struct tid_info *t,
unsigned int tid)
{
- return tid < t->ntids ? &(t->tid_tab[tid]) : NULL;
+ struct t3c_tid_entry *t3c_tid = tid < t->ntids ?
+ &(t->tid_tab[tid]) : NULL;
+
+ return (t3c_tid && t3c_tid->client) ? t3c_tid : NULL;
}
/*
diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c
index 48649244673e..ebcf35e4cf5b 100644
--- a/drivers/net/cxgb3/cxgb3_offload.c
+++ b/drivers/net/cxgb3/cxgb3_offload.c
@@ -508,6 +508,7 @@ void cxgb3_queue_tid_release(struct t3cdev *tdev, unsigned int tid)
spin_lock_bh(&td->tid_release_lock);
p->ctx = (void *)td->tid_release_list;
+ p->client = NULL;
td->tid_release_list = p;
if (!p->ctx)
schedule_work(&td->tid_release_task);
@@ -623,7 +624,8 @@ static int do_act_open_rpl(struct t3cdev *dev, struct sk_buff *skb)
struct t3c_tid_entry *t3c_tid;
t3c_tid = lookup_atid(&(T3C_DATA(dev))->tid_maps, atid);
- if (t3c_tid->ctx && t3c_tid->client && t3c_tid->client->handlers &&
+ if (t3c_tid && t3c_tid->ctx && t3c_tid->client &&
+ t3c_tid->client->handlers &&
t3c_tid->client->handlers[CPL_ACT_OPEN_RPL]) {
return t3c_tid->client->handlers[CPL_ACT_OPEN_RPL] (dev, skb,
t3c_tid->
@@ -642,7 +644,7 @@ static int do_stid_rpl(struct t3cdev *dev, struct sk_buff *skb)
struct t3c_tid_entry *t3c_tid;
t3c_tid = lookup_stid(&(T3C_DATA(dev))->tid_maps, stid);
- if (t3c_tid->ctx && t3c_tid->client->handlers &&
+ if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers &&
t3c_tid->client->handlers[p->opcode]) {
return t3c_tid->client->handlers[p->opcode] (dev, skb,
t3c_tid->ctx);
@@ -660,7 +662,7 @@ static int do_hwtid_rpl(struct t3cdev *dev, struct sk_buff *skb)
struct t3c_tid_entry *t3c_tid;
t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid);
- if (t3c_tid->ctx && t3c_tid->client->handlers &&
+ if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers &&
t3c_tid->client->handlers[p->opcode]) {
return t3c_tid->client->handlers[p->opcode]
(dev, skb, t3c_tid->ctx);
@@ -689,6 +691,28 @@ static int do_cr(struct t3cdev *dev, struct sk_buff *skb)
}
}
+/*
+ * Returns an sk_buff for a reply CPL message of size len. If the input
+ * sk_buff has no other users it is trimmed and reused, otherwise a new buffer
+ * is allocated. The input skb must be of size at least len. Note that this
+ * operation does not destroy the original skb data even if it decides to reuse
+ * the buffer.
+ */
+static struct sk_buff *cxgb3_get_cpl_reply_skb(struct sk_buff *skb, size_t len,
+ int gfp)
+{
+ if (likely(!skb_cloned(skb))) {
+ BUG_ON(skb->len < len);
+ __skb_trim(skb, len);
+ skb_get(skb);
+ } else {
+ skb = alloc_skb(len, gfp);
+ if (skb)
+ __skb_put(skb, len);
+ }
+ return skb;
+}
+
static int do_abort_req_rss(struct t3cdev *dev, struct sk_buff *skb)
{
union opcode_tid *p = cplhdr(skb);
@@ -696,30 +720,39 @@ static int do_abort_req_rss(struct t3cdev *dev, struct sk_buff *skb)
struct t3c_tid_entry *t3c_tid;
t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid);
- if (t3c_tid->ctx && t3c_tid->client->handlers &&
+ if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers &&
t3c_tid->client->handlers[p->opcode]) {
return t3c_tid->client->handlers[p->opcode]
(dev, skb, t3c_tid->ctx);
} else {
struct cpl_abort_req_rss *req = cplhdr(skb);
struct cpl_abort_rpl *rpl;
+ struct sk_buff *reply_skb;
+ unsigned int tid = GET_TID(req);
+ u8 cmd = req->status;
+
+ if (req->status == CPL_ERR_RTX_NEG_ADVICE ||
+ req->status == CPL_ERR_PERSIST_NEG_ADVICE)
+ goto out;
- struct sk_buff *skb =
- alloc_skb(sizeof(struct cpl_abort_rpl), GFP_ATOMIC);
- if (!skb) {
+ reply_skb = cxgb3_get_cpl_reply_skb(skb,
+ sizeof(struct
+ cpl_abort_rpl),
+ GFP_ATOMIC);
+
+ if (!reply_skb) {
printk("do_abort_req_rss: couldn't get skb!\n");
goto out;
}
- skb->priority = CPL_PRIORITY_DATA;
- __skb_put(skb, sizeof(struct cpl_abort_rpl));
- rpl = cplhdr(skb);
+ reply_skb->priority = CPL_PRIORITY_DATA;
+ __skb_put(reply_skb, sizeof(struct cpl_abort_rpl));
+ rpl = cplhdr(reply_skb);
rpl->wr.wr_hi =
htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL));
- rpl->wr.wr_lo = htonl(V_WR_TID(GET_TID(req)));
- OPCODE_TID(rpl) =
- htonl(MK_OPCODE_TID(CPL_ABORT_RPL, GET_TID(req)));
- rpl->cmd = req->status;
- cxgb3_ofld_send(dev, skb);
+ rpl->wr.wr_lo = htonl(V_WR_TID(tid));
+ OPCODE_TID(rpl) = htonl(MK_OPCODE_TID(CPL_ABORT_RPL, tid));
+ rpl->cmd = cmd;
+ cxgb3_ofld_send(dev, reply_skb);
out:
return CPL_RET_BUF_DONE;
}
@@ -732,7 +765,7 @@ static int do_act_establish(struct t3cdev *dev, struct sk_buff *skb)
struct t3c_tid_entry *t3c_tid;
t3c_tid = lookup_atid(&(T3C_DATA(dev))->tid_maps, atid);
- if (t3c_tid->ctx && t3c_tid->client->handlers &&
+ if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers &&
t3c_tid->client->handlers[CPL_ACT_ESTABLISH]) {
return t3c_tid->client->handlers[CPL_ACT_ESTABLISH]
(dev, skb, t3c_tid->ctx);
@@ -750,7 +783,7 @@ static int do_trace(struct t3cdev *dev, struct sk_buff *skb)
skb->protocol = htons(0xffff);
skb->dev = dev->lldev;
skb_pull(skb, sizeof(*p));
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
netif_receive_skb(skb);
return 0;
}
@@ -762,7 +795,7 @@ static int do_term(struct t3cdev *dev, struct sk_buff *skb)
struct t3c_tid_entry *t3c_tid;
t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid);
- if (t3c_tid->ctx && t3c_tid->client->handlers &&
+ if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers &&
t3c_tid->client->handlers[opcode]) {
return t3c_tid->client->handlers[opcode] (dev, skb,
t3c_tid->ctx);
@@ -961,7 +994,7 @@ void cxgb_redirect(struct dst_entry *old, struct dst_entry *new)
for (tid = 0; tid < ti->ntids; tid++) {
te = lookup_tid(ti, tid);
BUG_ON(!te);
- if (te->ctx && te->client && te->client->redirect) {
+ if (te && te->ctx && te->client && te->client->redirect) {
update_tcb = te->client->redirect(te->ctx, old, new, e);
if (update_tcb) {
l2t_hold(L2DATA(tdev), e);
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c
index 027ab2c3825c..3666586a4831 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/cxgb3/sge.c
@@ -661,7 +661,7 @@ static inline struct sk_buff *get_imm_packet(const struct rsp_desc *resp)
if (skb) {
__skb_put(skb, IMMED_PKT_SIZE);
- memcpy(skb->data, resp->imm_data, IMMED_PKT_SIZE);
+ skb_copy_to_linear_data(skb, resp->imm_data, IMMED_PKT_SIZE);
}
return skb;
}
@@ -897,11 +897,11 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
d->flit[2] = 0;
cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT_LSO);
hdr->cntrl = htonl(cntrl);
- eth_type = skb->nh.raw - skb->data == ETH_HLEN ?
+ eth_type = skb_network_offset(skb) == ETH_HLEN ?
CPL_ETH_II : CPL_ETH_II_VLAN;
tso_info |= V_LSO_ETH_TYPE(eth_type) |
- V_LSO_IPHDR_WORDS(skb->nh.iph->ihl) |
- V_LSO_TCPHDR_WORDS(skb->h.th->doff);
+ V_LSO_IPHDR_WORDS(ip_hdr(skb)->ihl) |
+ V_LSO_TCPHDR_WORDS(tcp_hdr(skb)->doff);
hdr->lso_info = htonl(tso_info);
flits = 3;
} else {
@@ -913,7 +913,8 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
if (skb->len <= WR_LEN - sizeof(*cpl)) {
q->sdesc[pidx].skb = NULL;
if (!skb->data_len)
- memcpy(&d->flit[2], skb->data, skb->len);
+ skb_copy_from_linear_data(skb, &d->flit[2],
+ skb->len);
else
skb_copy_bits(skb, 0, &d->flit[2], skb->len);
@@ -1319,16 +1320,19 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb,
/* Only TX_DATA builds SGLs */
from = (struct work_request_hdr *)skb->data;
- memcpy(&d->flit[1], &from[1], skb->h.raw - skb->data - sizeof(*from));
+ memcpy(&d->flit[1], &from[1],
+ skb_transport_offset(skb) - sizeof(*from));
- flits = (skb->h.raw - skb->data) / 8;
+ flits = skb_transport_offset(skb) / 8;
sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl;
- sgl_flits = make_sgl(skb, sgp, skb->h.raw, skb->tail - skb->h.raw,
+ sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb),
+ skb->tail - skb->transport_header,
adap->pdev);
if (need_skb_unmap()) {
setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits);
skb->destructor = deferred_unmap_destructor;
- ((struct unmap_info *)skb->cb)->len = skb->tail - skb->h.raw;
+ ((struct unmap_info *)skb->cb)->len = (skb->tail -
+ skb->transport_header);
}
write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits,
@@ -1349,8 +1353,8 @@ static inline unsigned int calc_tx_descs_ofld(const struct sk_buff *skb)
if (skb->len <= WR_LEN && cnt == 0)
return 1; /* packet fits as immediate data */
- flits = (skb->h.raw - skb->data) / 8; /* headers */
- if (skb->tail != skb->h.raw)
+ flits = skb_transport_offset(skb) / 8; /* headers */
+ if (skb->tail != skb->transport_header)
cnt++;
return flits_to_desc(flits + sgl_len(cnt));
}
@@ -1620,7 +1624,9 @@ static inline int rx_offload(struct t3cdev *tdev, struct sge_rspq *rq,
unsigned int gather_idx)
{
rq->offload_pkts++;
- skb->mac.raw = skb->nh.raw = skb->h.raw = skb->data;
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
if (rq->polling) {
rx_gather[gather_idx++] = skb;
@@ -1684,9 +1690,8 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
struct port_info *pi;
skb_pull(skb, sizeof(*p) + pad);
- skb->dev = adap->port[p->iff];
skb->dev->last_rx = jiffies;
- skb->protocol = eth_type_trans(skb, skb->dev);
+ skb->protocol = eth_type_trans(skb, adap->port[p->iff]);
pi = netdev_priv(skb->dev);
if (pi->rx_csum_offload && p->csum_valid && p->csum == 0xffff &&
!p->fragment) {
@@ -1717,11 +1722,11 @@ static void skb_data_init(struct sk_buff *skb, struct sge_fl_page *p,
{
skb->len = len;
if (len <= SKB_DATA_SIZE) {
- memcpy(skb->data, p->va, len);
+ skb_copy_to_linear_data(skb, p->va, len);
skb->tail += len;
put_page(p->frag.page);
} else {
- memcpy(skb->data, p->va, SKB_DATA_SIZE);
+ skb_copy_to_linear_data(skb, p->va, SKB_DATA_SIZE);
skb_shinfo(skb)->frags[0].page = p->frag.page;
skb_shinfo(skb)->frags[0].page_offset =
p->frag.page_offset + SKB_DATA_SIZE;
@@ -1767,7 +1772,7 @@ static struct sk_buff *get_packet(struct adapter *adap, struct sge_fl *fl,
__skb_put(skb, len);
pci_dma_sync_single_for_cpu(adap->pdev, mapping, len,
PCI_DMA_FROMDEVICE);
- memcpy(skb->data, sd->t.skb->data, len);
+ skb_copy_from_linear_data(sd->t.skb, skb->data, len);
pci_dma_sync_single_for_device(adap->pdev, mapping, len,
PCI_DMA_FROMDEVICE);
} else if (!drop_thres)
diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c
index d83f075ef2d7..fb485d0a43d8 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/cxgb3/t3_hw.c
@@ -1523,19 +1523,25 @@ static int mac_intr_handler(struct adapter *adap, unsigned int idx)
*/
int t3_phy_intr_handler(struct adapter *adapter)
{
- static const int intr_gpio_bits[] = { 8, 0x20 };
-
+ u32 mask, gpi = adapter_info(adapter)->gpio_intr;
u32 i, cause = t3_read_reg(adapter, A_T3DBG_INT_CAUSE);
for_each_port(adapter, i) {
- if (cause & intr_gpio_bits[i]) {
- struct cphy *phy = &adap2pinfo(adapter, i)->phy;
- int phy_cause = phy->ops->intr_handler(phy);
+ struct port_info *p = adap2pinfo(adapter, i);
+
+ mask = gpi - (gpi & (gpi - 1));
+ gpi -= mask;
+
+ if (!(p->port_type->caps & SUPPORTED_IRQ))
+ continue;
+
+ if (cause & mask) {
+ int phy_cause = p->phy.ops->intr_handler(&p->phy);
if (phy_cause & cphy_cause_link_change)
t3_link_changed(adapter, i);
if (phy_cause & cphy_cause_fifo_error)
- phy->fifo_errors++;
+ p->phy.fifo_errors++;
}
}
diff --git a/drivers/net/de600.c b/drivers/net/de600.c
index e547ce14eefe..dae97b860daa 100644
--- a/drivers/net/de600.c
+++ b/drivers/net/de600.c
@@ -359,7 +359,6 @@ static void de600_rx_intr(struct net_device *dev)
}
/* else */
- skb->dev = dev;
skb_reserve(skb,2); /* Align */
/* 'skb->data' points to the start of sk_buff data area. */
diff --git a/drivers/net/de620.c b/drivers/net/de620.c
index b6ad0cb50552..dc4892426174 100644
--- a/drivers/net/de620.c
+++ b/drivers/net/de620.c
@@ -697,7 +697,6 @@ static int de620_rx_intr(struct net_device *dev)
}
else { /* Yep! Go get it! */
skb_reserve(skb,2); /* Align */
- skb->dev = dev;
/* skb->data points to the start of sk_buff data area */
buffer = skb_put(skb,size);
/* copy the packet into the buffer */
diff --git a/drivers/net/declance.c b/drivers/net/declance.c
index 9f7e1db8ce62..95d854e2295c 100644
--- a/drivers/net/declance.c
+++ b/drivers/net/declance.c
@@ -616,7 +616,6 @@ static int lance_rx(struct net_device *dev)
}
lp->stats.rx_bytes += len;
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align */
skb_put(skb, len); /* make room */
diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c
index 07d2731c1aa8..571d82f8008c 100644
--- a/drivers/net/defxx.c
+++ b/drivers/net/defxx.c
@@ -3091,13 +3091,13 @@ static void dfx_rcv_queue_process(
{
/* Receive buffer allocated, pass receive packet up */
- memcpy(skb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3);
+ skb_copy_to_linear_data(skb,
+ p_buff + RCV_BUFF_K_PADDING,
+ pkt_len + 3);
}
skb_reserve(skb,3); /* adjust data field so that it points to FC byte */
skb_put(skb, pkt_len); /* pass up packet length, NOT including CRC */
- skb->dev = bp->dev; /* pass up device pointer */
-
skb->protocol = fddi_type_trans(skb, bp->dev);
bp->rcv_total_bytes += skb->len;
netif_rx(skb);
diff --git a/drivers/net/depca.c b/drivers/net/depca.c
index 5113eef755b9..183497020bfc 100644
--- a/drivers/net/depca.c
+++ b/drivers/net/depca.c
@@ -1044,7 +1044,6 @@ static int depca_rx(struct net_device *dev)
unsigned char *buf;
skb_reserve(skb, 2); /* 16 byte align the IP header */
buf = skb_put(skb, pkt_len);
- skb->dev = dev;
if (entry < lp->rx_old) { /* Wrapped buffer */
len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ;
memcpy_fromio(buf, lp->rx_buff[lp->rx_old], len);
@@ -1491,8 +1490,9 @@ static void __init depca_platform_probe (void)
depca_io_ports[i].device = pldev;
if (platform_device_add(pldev)) {
- platform_device_put(pldev);
depca_io_ports[i].device = NULL;
+ pldev->dev.platform_data = NULL;
+ platform_device_put(pldev);
continue;
}
diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c
index a79520295fd0..df62c0232f36 100644
--- a/drivers/net/dgrs.c
+++ b/drivers/net/dgrs.c
@@ -503,7 +503,6 @@ dgrs_rcv_frame(
/* discarding the frame */
goto out;
}
- skb->dev = devN;
skb_reserve(skb, 2); /* Align IP header */
again:
@@ -742,7 +741,7 @@ static int dgrs_start_xmit(struct sk_buff *skb, struct net_device *devN)
}
amt = min_t(unsigned int, len, rbdp->size - count);
- memcpy( (char *) S2H(rbdp->buf) + count, skb->data + i, amt);
+ skb_copy_from_linear_data_offset(skb, i, S2H(rbdp->buf) + count, amt);
i += amt;
count += amt;
len -= amt;
diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c
index 9d446a0fe0bf..74ec64a1625d 100644
--- a/drivers/net/dl2k.c
+++ b/drivers/net/dl2k.c
@@ -504,7 +504,6 @@ rio_timer (unsigned long data)
break;
}
np->rx_skbuff[entry] = skb;
- skb->dev = dev;
/* 16 byte align the IP header */
skb_reserve (skb, 2);
np->rx_ring[entry].fraginfo =
@@ -575,7 +574,6 @@ alloc_list (struct net_device *dev)
dev->name);
break;
}
- skb->dev = dev; /* Mark as being used by this device. */
skb_reserve (skb, 2); /* 16 byte align the IP header. */
/* Rubicon now supports 40 bits of addressing space. */
np->rx_ring[i].fraginfo =
@@ -866,7 +864,6 @@ receive_packet (struct net_device *dev)
DMA_48BIT_MASK,
np->rx_buf_sz,
PCI_DMA_FROMDEVICE);
- skb->dev = dev;
/* 16 byte align the IP header */
skb_reserve (skb, 2);
eth_copy_and_sum (skb,
@@ -910,7 +907,6 @@ receive_packet (struct net_device *dev)
break;
}
np->rx_skbuff[entry] = skb;
- skb->dev = dev;
/* 16 byte align the IP header */
skb_reserve (skb, 2);
np->rx_ring[entry].fraginfo =
diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c
index 615d2b14efa7..8cc1174e7f64 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/dm9000.c
@@ -954,7 +954,6 @@ dm9000_rx(struct net_device *dev)
/* Move data from DM9000 */
if (GoodPacket
&& ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
- skb->dev = dev;
skb_reserve(skb, 2);
rdptr = (u8 *) skb_put(skb, RxLen - 4);
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index 0cefef5e3f06..4d0e0aea72bf 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1769,7 +1769,7 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)
/* Align, init, and map the RFD. */
skb_reserve(rx->skb, NET_IP_ALIGN);
- memcpy(rx->skb->data, &nic->blank_rfd, sizeof(struct rfd));
+ skb_copy_to_linear_data(rx->skb, &nic->blank_rfd, sizeof(struct rfd));
rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data,
RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index 1d08e937af82..9267f16b1b32 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -409,25 +409,21 @@ e1000_release_hw_control(struct e1000_adapter *adapter)
{
uint32_t ctrl_ext;
uint32_t swsm;
- uint32_t extcnf;
/* Let firmware taken over control of h/w */
switch (adapter->hw.mac_type) {
- case e1000_82571:
- case e1000_82572:
- case e1000_80003es2lan:
- ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
- E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
- ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
- break;
case e1000_82573:
swsm = E1000_READ_REG(&adapter->hw, SWSM);
E1000_WRITE_REG(&adapter->hw, SWSM,
swsm & ~E1000_SWSM_DRV_LOAD);
+ break;
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_80003es2lan:
case e1000_ich8lan:
- extcnf = E1000_READ_REG(&adapter->hw, CTRL_EXT);
+ ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
- extcnf & ~E1000_CTRL_EXT_DRV_LOAD);
+ ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
break;
default:
break;
@@ -450,26 +446,21 @@ e1000_get_hw_control(struct e1000_adapter *adapter)
{
uint32_t ctrl_ext;
uint32_t swsm;
- uint32_t extcnf;
/* Let firmware know the driver has taken over */
switch (adapter->hw.mac_type) {
- case e1000_82571:
- case e1000_82572:
- case e1000_80003es2lan:
- ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
- E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
- ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
- break;
case e1000_82573:
swsm = E1000_READ_REG(&adapter->hw, SWSM);
E1000_WRITE_REG(&adapter->hw, SWSM,
swsm | E1000_SWSM_DRV_LOAD);
break;
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_80003es2lan:
case e1000_ich8lan:
- extcnf = E1000_READ_REG(&adapter->hw, EXTCNF_CTRL);
- E1000_WRITE_REG(&adapter->hw, EXTCNF_CTRL,
- extcnf | E1000_EXTCNF_CTRL_SWFLAG);
+ ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
+ E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
+ ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
break;
default:
break;
@@ -522,14 +513,15 @@ e1000_release_manageability(struct e1000_adapter *adapter)
}
}
-int
-e1000_up(struct e1000_adapter *adapter)
+/**
+ * e1000_configure - configure the hardware for RX and TX
+ * @adapter = private board structure
+ **/
+static void e1000_configure(struct e1000_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
int i;
- /* hardware has been reset, we need to reload some things */
-
e1000_set_multi(netdev);
e1000_restore_vlan(adapter);
@@ -548,14 +540,20 @@ e1000_up(struct e1000_adapter *adapter)
}
adapter->tx_queue_len = netdev->tx_queue_len;
+}
+
+int e1000_up(struct e1000_adapter *adapter)
+{
+ /* hardware has been reset, we need to reload some things */
+ e1000_configure(adapter);
+
+ clear_bit(__E1000_DOWN, &adapter->flags);
#ifdef CONFIG_E1000_NAPI
- netif_poll_enable(netdev);
+ netif_poll_enable(adapter->netdev);
#endif
e1000_irq_enable(adapter);
- clear_bit(__E1000_DOWN, &adapter->flags);
-
/* fire a link change interrupt to start the watchdog */
E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC);
return 0;
@@ -640,15 +638,15 @@ e1000_down(struct e1000_adapter *adapter)
* reschedule our watchdog timer */
set_bit(__E1000_DOWN, &adapter->flags);
+#ifdef CONFIG_E1000_NAPI
+ netif_poll_disable(netdev);
+#endif
e1000_irq_disable(adapter);
del_timer_sync(&adapter->tx_fifo_stall_timer);
del_timer_sync(&adapter->watchdog_timer);
del_timer_sync(&adapter->phy_info_timer);
-#ifdef CONFIG_E1000_NAPI
- netif_poll_disable(netdev);
-#endif
netdev->tx_queue_len = adapter->tx_queue_len;
adapter->link_speed = 0;
adapter->link_duplex = 0;
@@ -1410,21 +1408,17 @@ e1000_open(struct net_device *netdev)
return -EBUSY;
/* allocate transmit descriptors */
- if ((err = e1000_setup_all_tx_resources(adapter)))
+ err = e1000_setup_all_tx_resources(adapter);
+ if (err)
goto err_setup_tx;
/* allocate receive descriptors */
- if ((err = e1000_setup_all_rx_resources(adapter)))
- goto err_setup_rx;
-
- err = e1000_request_irq(adapter);
+ err = e1000_setup_all_rx_resources(adapter);
if (err)
- goto err_req_irq;
+ goto err_setup_rx;
e1000_power_up_phy(adapter);
- if ((err = e1000_up(adapter)))
- goto err_up;
adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
if ((adapter->hw.mng_cookie.status &
E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) {
@@ -1437,12 +1431,33 @@ e1000_open(struct net_device *netdev)
e1000_check_mng_mode(&adapter->hw))
e1000_get_hw_control(adapter);
+ /* before we allocate an interrupt, we must be ready to handle it.
+ * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt
+ * as soon as we call pci_request_irq, so we have to setup our
+ * clean_rx handler before we do so. */
+ e1000_configure(adapter);
+
+ err = e1000_request_irq(adapter);
+ if (err)
+ goto err_req_irq;
+
+ /* From here on the code is the same as e1000_up() */
+ clear_bit(__E1000_DOWN, &adapter->flags);
+
+#ifdef CONFIG_E1000_NAPI
+ netif_poll_enable(netdev);
+#endif
+
+ e1000_irq_enable(adapter);
+
+ /* fire a link status change interrupt to start the watchdog */
+ E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC);
+
return E1000_SUCCESS;
-err_up:
- e1000_power_down_phy(adapter);
- e1000_free_irq(adapter);
err_req_irq:
+ e1000_release_hw_control(adapter);
+ e1000_power_down_phy(adapter);
e1000_free_all_rx_resources(adapter);
err_setup_rx:
e1000_free_all_tx_resources(adapter);
@@ -2887,33 +2902,30 @@ e1000_tso(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring,
return err;
}
- hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
mss = skb_shinfo(skb)->gso_size;
if (skb->protocol == htons(ETH_P_IP)) {
- skb->nh.iph->tot_len = 0;
- skb->nh.iph->check = 0;
- skb->h.th->check =
- ~csum_tcpudp_magic(skb->nh.iph->saddr,
- skb->nh.iph->daddr,
- 0,
- IPPROTO_TCP,
- 0);
+ struct iphdr *iph = ip_hdr(skb);
+ iph->tot_len = 0;
+ iph->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0,
+ IPPROTO_TCP,
+ 0);
cmd_length = E1000_TXD_CMD_IP;
- ipcse = skb->h.raw - skb->data - 1;
+ ipcse = skb_transport_offset(skb) - 1;
} else if (skb->protocol == htons(ETH_P_IPV6)) {
- skb->nh.ipv6h->payload_len = 0;
- skb->h.th->check =
- ~csum_ipv6_magic(&skb->nh.ipv6h->saddr,
- &skb->nh.ipv6h->daddr,
- 0,
- IPPROTO_TCP,
- 0);
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
ipcse = 0;
}
- ipcss = skb->nh.raw - skb->data;
- ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data;
- tucss = skb->h.raw - skb->data;
- tucso = (void *)&(skb->h.th->check) - (void *)skb->data;
+ ipcss = skb_network_offset(skb);
+ ipcso = (void *)&(ip_hdr(skb)->check) - (void *)skb->data;
+ tucss = skb_transport_offset(skb);
+ tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data;
tucse = 0;
cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE |
@@ -2954,7 +2966,7 @@ e1000_tx_csum(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring,
uint8_t css;
if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
- css = skb->h.raw - skb->data;
+ css = skb_transport_offset(skb);
i = tx_ring->next_to_use;
buffer_info = &tx_ring->buffer_info[i];
@@ -2962,7 +2974,8 @@ e1000_tx_csum(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring,
context_desc->lower_setup.ip_config = 0;
context_desc->upper_setup.tcp_fields.tucss = css;
- context_desc->upper_setup.tcp_fields.tucso = css + skb->csum;
+ context_desc->upper_setup.tcp_fields.tucso =
+ css + skb->csum_offset;
context_desc->upper_setup.tcp_fields.tucse = 0;
context_desc->tcp_seg_setup.data = 0;
context_desc->cmd_and_length = cpu_to_le32(E1000_TXD_CMD_DEXT);
@@ -3296,7 +3309,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
/* TSO Workaround for 82571/2/3 Controllers -- if skb->data
* points to just header, pull a few bytes of payload from
* frags into skb->data */
- hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
if (skb->data_len && (hdr_len == (skb->len - skb->data_len))) {
switch (adapter->hw.mac_type) {
unsigned int pull_size;
@@ -3307,7 +3320,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
* NOTE: this is a TSO only workaround
* if end byte alignment not correct move us
* into the next dword */
- if ((unsigned long)(skb->tail - 1) & 4)
+ if ((unsigned long)(skb_tail_pointer(skb) - 1) & 4)
break;
/* fall through */
case e1000_82571:
@@ -3363,12 +3376,9 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
(adapter->hw.mac_type == e1000_82573))
e1000_transfer_dhcp_info(adapter, skb);
- local_irq_save(flags);
- if (!spin_trylock(&tx_ring->tx_lock)) {
+ if (!spin_trylock_irqsave(&tx_ring->tx_lock, flags))
/* Collision - tell upper layer to requeue */
- local_irq_restore(flags);
return NETDEV_TX_LOCKED;
- }
/* need: count + 2 desc gap to keep tail from touching
* head, otherwise try next time */
@@ -3796,7 +3806,7 @@ e1000_intr_msi(int irq, void *data)
for (i = 0; i < E1000_MAX_INTR; i++)
if (unlikely(!adapter->clean_rx(adapter, adapter->rx_ring) &
- e1000_clean_tx_irq(adapter, adapter->tx_ring)))
+ !e1000_clean_tx_irq(adapter, adapter->tx_ring)))
break;
if (likely(adapter->itr_setting & 3))
@@ -3899,7 +3909,7 @@ e1000_intr(int irq, void *data)
for (i = 0; i < E1000_MAX_INTR; i++)
if (unlikely(!adapter->clean_rx(adapter, adapter->rx_ring) &
- e1000_clean_tx_irq(adapter, adapter->tx_ring)))
+ !e1000_clean_tx_irq(adapter, adapter->tx_ring)))
break;
if (likely(adapter->itr_setting & 3))
@@ -3949,7 +3959,7 @@ e1000_clean(struct net_device *poll_dev, int *budget)
poll_dev->quota -= work_done;
/* If no Tx and not enough Rx work done, exit the polling mode */
- if ((tx_cleaned && (work_done < work_to_do)) ||
+ if ((!tx_cleaned && (work_done == 0)) ||
!netif_running(poll_dev)) {
quit_polling:
if (likely(adapter->itr_setting & 3))
@@ -3979,7 +3989,7 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter,
#ifdef CONFIG_E1000_NAPI
unsigned int count = 0;
#endif
- boolean_t cleaned = TRUE;
+ boolean_t cleaned = FALSE;
unsigned int total_tx_bytes=0, total_tx_packets=0;
i = tx_ring->next_to_clean;
@@ -4013,10 +4023,7 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter,
#ifdef CONFIG_E1000_NAPI
#define E1000_TX_WEIGHT 64
/* weight of a sort for tx, to avoid endless transmit cleanup */
- if (count++ == E1000_TX_WEIGHT) {
- cleaned = FALSE;
- break;
- }
+ if (count++ == E1000_TX_WEIGHT) break;
#endif
}
@@ -4230,9 +4237,12 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter,
netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
if (new_skb) {
skb_reserve(new_skb, NET_IP_ALIGN);
- memcpy(new_skb->data - NET_IP_ALIGN,
- skb->data - NET_IP_ALIGN,
- length + NET_IP_ALIGN);
+ skb_copy_to_linear_data_offset(new_skb,
+ -NET_IP_ALIGN,
+ (skb->data -
+ NET_IP_ALIGN),
+ (length +
+ NET_IP_ALIGN));
/* save the skb in buffer_info as good */
buffer_info->skb = skb;
skb = new_skb;
@@ -4394,7 +4404,7 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter,
PCI_DMA_FROMDEVICE);
vaddr = kmap_atomic(ps_page->ps_page[0],
KM_SKB_DATA_SOFTIRQ);
- memcpy(skb->tail, vaddr, l1);
+ memcpy(skb_tail_pointer(skb), vaddr, l1);
kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ);
pci_dma_sync_single_for_device(pdev,
ps_page_dma->ps_page_dma[0],
diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c
index b4463094c93a..39654e1e2bed 100644
--- a/drivers/net/eepro.c
+++ b/drivers/net/eepro.c
@@ -1591,7 +1591,6 @@ eepro_rx(struct net_device *dev)
break;
}
- skb->dev = dev;
skb_reserve(skb,2);
if (lp->version == LAN595)
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
index e28bb1e38f8d..6c267c38df97 100644
--- a/drivers/net/eepro100.c
+++ b/drivers/net/eepro100.c
@@ -1793,7 +1793,6 @@ speedo_rx(struct net_device *dev)
copying to a properly sized skbuff. */
if (pkt_len < rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
- skb->dev = dev;
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
/* 'skb_put()' points to the start of sk_buff data area. */
pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry],
@@ -1805,8 +1804,9 @@ speedo_rx(struct net_device *dev)
eth_copy_and_sum(skb, sp->rx_skbuff[entry]->data, pkt_len, 0);
skb_put(skb, pkt_len);
#else
- memcpy(skb_put(skb, pkt_len), sp->rx_skbuff[entry]->data,
- pkt_len);
+ skb_copy_from_linear_data(sp->rx_skbuff[entry],
+ skb_put(skb, pkt_len),
+ pkt_len);
#endif
pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry],
sizeof(struct RxFD) + pkt_len,
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 3868b8031266..8aaf5ec0c360 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -976,7 +976,6 @@ static void eexp_hw_rx_pio(struct net_device *dev)
lp->stats.rx_dropped++;
break;
}
- skb->dev = dev;
skb_reserve(skb, 2);
outw(pbuf+10, ioaddr+READ_PTR);
insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1);
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 0e4042bc0a48..58364a0ff378 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -391,8 +391,8 @@ static int ehea_poll(struct net_device *dev, int *budget)
if (!skb)
break;
}
- memcpy(skb->data, ((char*)cqe) + 64,
- cqe->num_bytes_transfered - 4);
+ skb_copy_to_linear_data(skb, ((char*)cqe) + 64,
+ cqe->num_bytes_transfered - 4);
ehea_fill_skb(dev, skb, cqe);
} else if (rq == 2) { /* RQ2 */
skb = get_skb_by_index(skb_arr_rq2,
@@ -1262,8 +1262,8 @@ static int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr)
static inline void write_ip_start_end(struct ehea_swqe *swqe,
const struct sk_buff *skb)
{
- swqe->ip_start = (u8)(((u64)skb->nh.iph) - ((u64)skb->data));
- swqe->ip_end = (u8)(swqe->ip_start + skb->nh.iph->ihl * 4 - 1);
+ swqe->ip_start = skb_network_offset(skb);
+ swqe->ip_end = (u8)(swqe->ip_start + ip_hdrlen(skb) - 1);
}
static inline void write_tcp_offset_end(struct ehea_swqe *swqe,
@@ -1300,13 +1300,13 @@ static void write_swqe2_TSO(struct sk_buff *skb,
/* copy only eth/ip/tcp headers to immediate data and
* the rest of skb->data to sg1entry
*/
- headersize = ETH_HLEN + (skb->nh.iph->ihl * 4) + (skb->h.th->doff * 4);
+ headersize = ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb);
skb_data_size = skb->len - skb->data_len;
if (skb_data_size >= headersize) {
/* copy immediate data */
- memcpy(imm_data, skb->data, headersize);
+ skb_copy_from_linear_data(skb, imm_data, headersize);
swqe->immediate_data_length = headersize;
if (skb_data_size > headersize) {
@@ -1337,7 +1337,7 @@ static void write_swqe2_nonTSO(struct sk_buff *skb,
*/
if (skb_data_size >= SWQE2_MAX_IMM) {
/* copy immediate data */
- memcpy(imm_data, skb->data, SWQE2_MAX_IMM);
+ skb_copy_from_linear_data(skb, imm_data, SWQE2_MAX_IMM);
swqe->immediate_data_length = SWQE2_MAX_IMM;
@@ -1350,7 +1350,7 @@ static void write_swqe2_nonTSO(struct sk_buff *skb,
swqe->descriptors++;
}
} else {
- memcpy(imm_data, skb->data, skb_data_size);
+ skb_copy_from_linear_data(skb, imm_data, skb_data_size);
swqe->immediate_data_length = skb_data_size;
}
}
@@ -1688,6 +1688,7 @@ static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
struct ehea_swqe *swqe, u32 lkey)
{
if (skb->protocol == htons(ETH_P_IP)) {
+ const struct iphdr *iph = ip_hdr(skb);
/* IPv4 */
swqe->tx_control |= EHEA_SWQE_CRC
| EHEA_SWQE_IP_CHECKSUM
@@ -1697,15 +1698,15 @@ static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
write_ip_start_end(swqe, skb);
- if (skb->nh.iph->protocol == IPPROTO_UDP) {
- if ((skb->nh.iph->frag_off & IP_MF) ||
- (skb->nh.iph->frag_off & IP_OFFSET))
+ if (iph->protocol == IPPROTO_UDP) {
+ if ((iph->frag_off & IP_MF) ||
+ (iph->frag_off & IP_OFFSET))
/* IP fragment, so don't change cs */
swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM;
else
write_udp_offset_end(swqe, skb);
- } else if (skb->nh.iph->protocol == IPPROTO_TCP) {
+ } else if (iph->protocol == IPPROTO_TCP) {
write_tcp_offset_end(swqe, skb);
}
@@ -1731,10 +1732,11 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
int i;
if (skb->protocol == htons(ETH_P_IP)) {
+ const struct iphdr *iph = ip_hdr(skb);
/* IPv4 */
write_ip_start_end(swqe, skb);
- if (skb->nh.iph->protocol == IPPROTO_TCP) {
+ if (iph->protocol == IPPROTO_TCP) {
swqe->tx_control |= EHEA_SWQE_CRC
| EHEA_SWQE_IP_CHECKSUM
| EHEA_SWQE_TCP_CHECKSUM
@@ -1742,9 +1744,9 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
write_tcp_offset_end(swqe, skb);
- } else if (skb->nh.iph->protocol == IPPROTO_UDP) {
- if ((skb->nh.iph->frag_off & IP_MF) ||
- (skb->nh.iph->frag_off & IP_OFFSET))
+ } else if (iph->protocol == IPPROTO_UDP) {
+ if ((iph->frag_off & IP_MF) ||
+ (iph->frag_off & IP_OFFSET))
/* IP fragment, so don't change cs */
swqe->tx_control |= EHEA_SWQE_CRC
| EHEA_SWQE_IMM_DATA_PRESENT;
@@ -1770,10 +1772,11 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
/* copy (immediate) data */
if (nfrags == 0) {
/* data is in a single piece */
- memcpy(imm_data, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, imm_data, skb->len);
} else {
/* first copy data from the skb->data buffer ... */
- memcpy(imm_data, skb->data, skb->len - skb->data_len);
+ skb_copy_from_linear_data(skb, imm_data,
+ skb->len - skb->data_len);
imm_data += skb->len - skb->data_len;
/* ... then copy data from the fragments */
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 3a6a83d3ee1c..4e3f14c9c717 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -934,7 +934,6 @@ static void epic_init_ring(struct net_device *dev)
ep->rx_skbuff[i] = skb;
if (skb == NULL)
break;
- skb->dev = dev; /* Mark as being used by this device. */
skb_reserve(skb, 2); /* 16 byte align the IP header. */
ep->rx_ring[i].bufaddr = pci_map_single(ep->pci_dev,
skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
@@ -1199,7 +1198,6 @@ static int epic_rx(struct net_device *dev, int budget)
to a minimally-sized skbuff. */
if (pkt_len < rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(ep->pci_dev,
ep->rx_ring[entry].bufaddr,
@@ -1236,7 +1234,6 @@ static int epic_rx(struct net_device *dev, int budget)
skb = ep->rx_skbuff[entry] = dev_alloc_skb(ep->rx_buf_sz);
if (skb == NULL)
break;
- skb->dev = dev; /* Mark as being used by this device. */
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
ep->rx_ring[entry].bufaddr = pci_map_single(ep->pci_dev,
skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c
index 93283e386f3a..04abf59e5007 100644
--- a/drivers/net/eth16i.c
+++ b/drivers/net/eth16i.c
@@ -1175,7 +1175,6 @@ static void eth16i_rx(struct net_device *dev)
break;
}
- skb->dev = dev;
skb_reserve(skb,2);
/*
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index 714ea1176ec7..cb0792c187ba 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -993,7 +993,6 @@ static int ewrk3_rx(struct net_device *dev)
if ((skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
unsigned char *p;
- skb->dev = dev;
skb_reserve(skb, 2); /* Align to 16 bytes */
p = skb_put(skb, pkt_len);
diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c
index 38a13f440530..abe9b089c610 100644
--- a/drivers/net/fealnx.c
+++ b/drivers/net/fealnx.c
@@ -1719,7 +1719,6 @@ static int netdev_rx(struct net_device *dev)
to a minimally-sized skbuff. */
if (pkt_len < rx_copybreak &&
(skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(np->pci_dev,
np->cur_rx->buffer,
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index 6764281b4531..255b09124e11 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -647,7 +647,6 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
fep->stats.rx_dropped++;
} else {
- skb->dev = dev;
skb_put(skb,pkt_len-4); /* Make room */
eth_copy_and_sum(skb, data, pkt_len-4, 0);
skb->protocol=eth_type_trans(skb,dev);
diff --git a/drivers/net/fec_8xx/fec_main.c b/drivers/net/fec_8xx/fec_main.c
index 77f747a5afa7..e824d5d231af 100644
--- a/drivers/net/fec_8xx/fec_main.c
+++ b/drivers/net/fec_8xx/fec_main.c
@@ -551,7 +551,9 @@ static int fec_enet_rx_common(struct net_device *dev, int *budget)
skbn = dev_alloc_skb(pkt_len + 2);
if (skbn != NULL) {
skb_reserve(skbn, 2); /* align IP header */
- memcpy(skbn->data, skb->data, pkt_len);
+ skb_copy_from_linear_data(skb
+ skbn->data,
+ pkt_len);
/* swap */
skbt = skb;
skb = skbn;
@@ -561,7 +563,6 @@ static int fec_enet_rx_common(struct net_device *dev, int *budget)
skbn = dev_alloc_skb(ENET_RX_FRSIZE);
if (skbn != NULL) {
- skb->dev = dev;
skb_put(skb, pkt_len); /* Make room */
skb->protocol = eth_type_trans(skb, dev);
received++;
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index d04214e4e581..7a018027fcc0 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -1385,11 +1385,12 @@ static int nv_alloc_rx(struct net_device *dev)
while (np->put_rx.orig != less_rx) {
struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD);
if (skb) {
- skb->dev = dev;
np->put_rx_ctx->skb = skb;
- np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data,
- skb->end-skb->data, PCI_DMA_FROMDEVICE);
- np->put_rx_ctx->dma_len = skb->end-skb->data;
+ np->put_rx_ctx->dma = pci_map_single(np->pci_dev,
+ skb->data,
+ skb_tailroom(skb),
+ PCI_DMA_FROMDEVICE);
+ np->put_rx_ctx->dma_len = skb_tailroom(skb);
np->put_rx.orig->buf = cpu_to_le32(np->put_rx_ctx->dma);
wmb();
np->put_rx.orig->flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL);
@@ -1416,11 +1417,12 @@ static int nv_alloc_rx_optimized(struct net_device *dev)
while (np->put_rx.ex != less_rx) {
struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD);
if (skb) {
- skb->dev = dev;
np->put_rx_ctx->skb = skb;
- np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data,
- skb->end-skb->data, PCI_DMA_FROMDEVICE);
- np->put_rx_ctx->dma_len = skb->end-skb->data;
+ np->put_rx_ctx->dma = pci_map_single(np->pci_dev,
+ skb->data,
+ skb_tailroom(skb),
+ PCI_DMA_FROMDEVICE);
+ np->put_rx_ctx->dma_len = skb_tailroom(skb);
np->put_rx.ex->bufhigh = cpu_to_le64(np->put_rx_ctx->dma) >> 32;
np->put_rx.ex->buflow = cpu_to_le64(np->put_rx_ctx->dma) & 0x0FFFFFFFF;
wmb();
@@ -1604,8 +1606,9 @@ static void nv_drain_rx(struct net_device *dev)
wmb();
if (np->rx_skb[i].skb) {
pci_unmap_single(np->pci_dev, np->rx_skb[i].dma,
- np->rx_skb[i].skb->end-np->rx_skb[i].skb->data,
- PCI_DMA_FROMDEVICE);
+ (skb_end_pointer(np->rx_skb[i].skb) -
+ np->rx_skb[i].skb->data),
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb(np->rx_skb[i].skb);
np->rx_skb[i].skb = NULL;
}
@@ -4376,11 +4379,12 @@ static int nv_loopback_test(struct net_device *dev)
ret = 0;
goto out;
}
+ test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data,
+ skb_tailroom(tx_skb),
+ PCI_DMA_FROMDEVICE);
pkt_data = skb_put(tx_skb, pkt_len);
for (i = 0; i < pkt_len; i++)
pkt_data[i] = (u8)(i & 0xff);
- test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data,
- tx_skb->end-tx_skb->data, PCI_DMA_FROMDEVICE);
if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
np->tx_ring.orig[0].buf = cpu_to_le32(test_dma_addr);
@@ -4437,7 +4441,7 @@ static int nv_loopback_test(struct net_device *dev)
}
pci_unmap_page(np->pci_dev, test_dma_addr,
- tx_skb->end-tx_skb->data,
+ (skb_end_pointer(tx_skb) - tx_skb->data),
PCI_DMA_TODEVICE);
dev_kfree_skb_any(tx_skb);
out:
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index 4a05c14bf7ec..e2ddd617493a 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -160,7 +160,8 @@ static int fs_enet_rx_napi(struct net_device *dev, int *budget)
skbn = dev_alloc_skb(pkt_len + 2);
if (skbn != NULL) {
skb_reserve(skbn, 2); /* align IP header */
- memcpy(skbn->data, skb->data, pkt_len);
+ skb_copy_from_linear_data(skb,
+ skbn->data, pkt_len);
/* swap */
skbt = skb;
skb = skbn;
@@ -170,7 +171,6 @@ static int fs_enet_rx_napi(struct net_device *dev, int *budget)
skbn = dev_alloc_skb(ENET_RX_FRSIZE);
if (skbn != NULL) {
- skb->dev = dev;
skb_put(skb, pkt_len); /* Make room */
skb->protocol = eth_type_trans(skb, dev);
received++;
@@ -294,7 +294,8 @@ static int fs_enet_rx_non_napi(struct net_device *dev)
skbn = dev_alloc_skb(pkt_len + 2);
if (skbn != NULL) {
skb_reserve(skbn, 2); /* align IP header */
- memcpy(skbn->data, skb->data, pkt_len);
+ skb_copy_from_linear_data(skb,
+ skbn->data, pkt_len);
/* swap */
skbt = skb;
skb = skbn;
@@ -304,7 +305,6 @@ static int fs_enet_rx_non_napi(struct net_device *dev)
skbn = dev_alloc_skb(ENET_RX_FRSIZE);
if (skbn != NULL) {
- skb->dev = dev;
skb_put(skb, pkt_len); /* Make room */
skb->protocol = eth_type_trans(skb, dev);
received++;
@@ -516,7 +516,6 @@ void fs_init_bds(struct net_device *dev)
break;
}
fep->rx_skbuff[i] = skb;
- skb->dev = dev;
CBDW_BUFADDR(bdp,
dma_map_single(fep->dev, skb->data,
L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index d981d4c41dd3..b666a0cc0642 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -942,18 +942,18 @@ static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb)
/* Tell the controller what the protocol is */
/* And provide the already calculated phcs */
- if (skb->nh.iph->protocol == IPPROTO_UDP) {
+ if (ip_hdr(skb)->protocol == IPPROTO_UDP) {
flags |= TXFCB_UDP;
- fcb->phcs = skb->h.uh->check;
+ fcb->phcs = udp_hdr(skb)->check;
} else
- fcb->phcs = skb->h.th->check;
+ fcb->phcs = udp_hdr(skb)->check;
/* l3os is the distance between the start of the
* frame (skb->data) and the start of the IP hdr.
* l4os is the distance between the start of the
* l3 hdr and the l4 hdr */
- fcb->l3os = (u16)(skb->nh.raw - skb->data - GMAC_FCB_LEN);
- fcb->l4os = (u16)(skb->h.raw - skb->nh.raw);
+ fcb->l3os = (u16)(skb_network_offset(skb) - GMAC_FCB_LEN);
+ fcb->l4os = skb_network_header_len(skb);
fcb->flags = flags;
}
@@ -1295,8 +1295,6 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
*/
skb_reserve(skb, alignamount);
- skb->dev = dev;
-
bdp->bufPtr = dma_map_single(NULL, skb->data,
priv->rx_buffer_size, DMA_FROM_DEVICE);
diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c
index c3c0d67fc383..2521b111b3a5 100644
--- a/drivers/net/hamachi.c
+++ b/drivers/net/hamachi.c
@@ -1568,7 +1568,6 @@ static int hamachi_rx(struct net_device *dev)
printk(KERN_ERR "%s: rx_copybreak non-zero "
"not good with RX_CHECKSUM\n", dev->name);
#endif
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(hmp->pci_dev,
hmp->rx_ring[entry].addr,
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
index 59214e74b9cf..30baf6ecfc63 100644
--- a/drivers/net/hamradio/baycom_ser_fdx.c
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -75,12 +75,14 @@
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/init.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
#include <linux/hdlcdrv.h>
#include <linux/baycom.h>
#include <linux/jiffies.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
/* --------------------------------------------------------------------- */
#define BAYCOM_DEBUG
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index d2542697e298..656f2789c9ba 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -282,7 +282,7 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev)
}
skb->protocol = ax25_type_trans(skb, dev);
- skb->nh.raw = skb->data;
+ skb_reset_network_header(skb);
dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
bpq->stats.tx_packets++;
bpq->stats.tx_bytes+=skb->len;
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index 0fbb414b5a4d..3be8c5047599 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -930,7 +930,7 @@ static int scc_send_packet(struct sk_buff *skb, struct net_device *dev)
/* Transfer data to DMA buffer */
i = priv->tx_head;
- memcpy(priv->tx_buf[i], skb->data + 1, skb->len - 1);
+ skb_copy_from_linear_data_offset(skb, 1, priv->tx_buf[i], skb->len - 1);
priv->tx_len[i] = skb->len - 1;
/* Clear interrupts while we touch our circular buffers */
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index f5a17ad9d3d6..b33adc6a340b 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -317,7 +317,9 @@ void hdlcdrv_transmitter(struct net_device *dev, struct hdlcdrv_state *s)
dev_kfree_skb_irq(skb);
break;
}
- memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
+ skb_copy_from_linear_data_offset(skb, 1,
+ s->hdlctx.buffer,
+ pkt_len);
dev_kfree_skb_irq(skb);
s->hdlctx.bp = s->hdlctx.buffer;
append_crc_ccitt(s->hdlctx.buffer, pkt_len);
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index ee3ea4fa729f..467559debfd6 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -638,7 +638,9 @@ static void yam_tx_byte(struct net_device *dev, struct yam_port *yp)
dev_kfree_skb_any(skb);
break;
}
- memcpy(yp->tx_buf, skb->data + 1, yp->tx_len);
+ skb_copy_from_linear_data_offset(skb, 1,
+ yp->tx_buf,
+ yp->tx_len);
dev_kfree_skb_any(skb);
yp->tx_count = 0;
yp->tx_crcl = 0x21;
diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c
index 7dc5185aa2c0..8118a6750b61 100644
--- a/drivers/net/hp100.c
+++ b/drivers/net/hp100.c
@@ -1816,7 +1816,6 @@ static void hp100_rx(struct net_device *dev)
u_char *ptr;
skb_reserve(skb,2);
- skb->dev = dev;
/* ptr to start of the sk_buff data area */
skb_put(skb, pkt_len);
diff --git a/drivers/net/ibm_emac/ibm_emac_core.c b/drivers/net/ibm_emac/ibm_emac_core.c
index dd8ad8746825..3d82d46f4998 100644
--- a/drivers/net/ibm_emac/ibm_emac_core.c
+++ b/drivers/net/ibm_emac/ibm_emac_core.c
@@ -1338,7 +1338,7 @@ static inline int emac_rx_sg_append(struct ocp_enet_private *dev, int slot)
dev_kfree_skb(dev->rx_sg_skb);
dev->rx_sg_skb = NULL;
} else {
- cacheable_memcpy(dev->rx_sg_skb->tail,
+ cacheable_memcpy(skb_tail_pointer(dev->rx_sg_skb),
dev->rx_skb[slot]->data, len);
skb_put(dev->rx_sg_skb, len);
emac_recycle_rx_skb(dev, slot, len);
@@ -1398,7 +1398,6 @@ static int emac_poll_rx(void *param, int budget)
skb_put(skb, len);
push_packet:
- skb->dev = dev->ndev;
skb->protocol = eth_type_trans(skb, dev->ndev);
emac_rx_csum(dev, skb, ctrl);
diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c
index 3f946c811511..fe85d6fcba33 100644
--- a/drivers/net/ibmlana.c
+++ b/drivers/net/ibmlana.c
@@ -601,7 +601,6 @@ static void irqrx_handler(struct net_device *dev)
/* set up skb fields */
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE;
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c
index 458db0538a9a..0573fcfcb2c4 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ibmveth.c
@@ -798,7 +798,6 @@ static int ibmveth_poll(struct net_device *netdev, int *budget)
skb_reserve(skb, offset);
skb_put(skb, length);
- skb->dev = netdev;
skb->protocol = eth_type_trans(skb, netdev);
netif_receive_skb(skb); /* send it up */
diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c
index 4ad780719a84..f749e07c6425 100644
--- a/drivers/net/ioc3-eth.c
+++ b/drivers/net/ioc3-eth.c
@@ -633,8 +633,6 @@ static inline void ioc3_rx(struct ioc3_private *ip)
ip->rx_skbs[rx_entry] = NULL; /* Poison */
- new_skb->dev = priv_netdev(ip);
-
/* Because we reserve afterwards. */
skb_put(new_skb, (1664 + RX_OFFSET));
rxb = (struct ioc3_erxbuf *) new_skb->data;
@@ -940,7 +938,6 @@ static void ioc3_alloc_rings(struct net_device *dev)
}
ip->rx_skbs[i] = skb;
- skb->dev = dev;
/* Because we reserve afterwards. */
skb_put(skb, (1664 + RX_OFFSET));
@@ -1396,9 +1393,9 @@ static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
* manually.
*/
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- int proto = ntohs(skb->nh.iph->protocol);
+ const struct iphdr *ih = ip_hdr(skb);
+ const int proto = ntohs(ih->protocol);
unsigned int csoff;
- struct iphdr *ih = skb->nh.iph;
uint32_t csum, ehsum;
uint16_t *eh;
@@ -1425,11 +1422,11 @@ static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
csoff = ETH_HLEN + (ih->ihl << 2);
if (proto == IPPROTO_UDP) {
csoff += offsetof(struct udphdr, check);
- skb->h.uh->check = csum;
+ udp_hdr(skb)->check = csum;
}
if (proto == IPPROTO_TCP) {
csoff += offsetof(struct tcphdr, check);
- skb->h.th->check = csum;
+ tcp_hdr(skb)->check = csum;
}
w0 = ETXD_DOCHECKSUM | (csoff << ETXD_CHKOFF_SHIFT);
@@ -1446,7 +1443,7 @@ static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (len <= 104) {
/* Short packet, let's copy it directly into the ring. */
- memcpy(desc->data, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, desc->data, skb->len);
if (len < ETH_ZLEN) {
/* Very short packet, pad with zeros at the end. */
memset(desc->data + len, 0, ETH_ZLEN - len);
diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c
index cebf8c374bc5..f9c889c0dd07 100644
--- a/drivers/net/irda/ali-ircc.c
+++ b/drivers/net/irda/ali-ircc.c
@@ -1472,9 +1472,8 @@ static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
self->stats.tx_bytes += skb->len;
- memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data,
- skb->len);
-
+ skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start,
+ skb->len);
self->tx_fifo.len++;
self->tx_fifo.free++;
@@ -1924,7 +1923,7 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self)
/* Copy frame without CRC, CRC is removed by hardware*/
skb_put(skb, len);
- memcpy(skb->data, self->rx_buff.data, len);
+ skb_copy_to_linear_data(skb, self->rx_buff.data, len);
/* Move to next frame */
self->rx_buff.data += len;
@@ -1932,7 +1931,7 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self)
self->stats.rx_packets++;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
self->netdev->last_rx = jiffies;
diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c
index 37914dc5b90e..4dbdfaaf37bf 100644
--- a/drivers/net/irda/au1k_ir.c
+++ b/drivers/net/irda/au1k_ir.c
@@ -526,7 +526,7 @@ static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
if (aup->speed == 4000000) {
/* FIR */
- memcpy((void *)pDB->vaddr, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, pDB->vaddr, skb->len);
ptxd->count_0 = skb->len & 0xff;
ptxd->count_1 = (skb->len >> 8) & 0xff;
@@ -604,9 +604,9 @@ static int au1k_irda_rx(struct net_device *dev)
skb_put(skb, count);
else
skb_put(skb, count-2);
- memcpy(skb->data, (void *)pDB->vaddr, count-2);
+ skb_copy_to_linear_data(skb, pDB->vaddr, count - 2);
skb->dev = dev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
prxd->count_0 = 0;
diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c
index 11af0ae7510e..3ca47bf6dfec 100644
--- a/drivers/net/irda/donauboe.c
+++ b/drivers/net/irda/donauboe.c
@@ -1119,7 +1119,7 @@ dumpbufs(skb->data,skb->len,'>');
else
{
len = skb->len;
- memcpy (self->tx_bufs[self->txs], skb->data, len);
+ skb_copy_from_linear_data(skb, self->tx_bufs[self->txs], len);
}
self->ring->tx[self->txs].len = len & 0x0fff;
@@ -1282,11 +1282,11 @@ dumpbufs(self->rx_bufs[self->rxs],len,'<');
skb_reserve (skb, 1);
skb_put (skb, len);
- memcpy (skb->data, self->rx_bufs[self->rxs], len);
-
+ skb_copy_to_linear_data(skb, self->rx_bufs[self->rxs],
+ len);
self->stats.rx_packets++;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons (ETH_P_IRDA);
}
else
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 1d510bdc9b84..0ac240ca905b 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -441,7 +441,7 @@ static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev)
goto drop;
}
- memcpy(self->tx_buff + self->header_length, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, self->tx_buff + self->header_length, skb->len);
/* Change setting for next frame */
if (self->capability & IUC_STIR421X) {
@@ -902,7 +902,7 @@ static void irda_usb_receive(struct urb *urb)
if(docopy) {
/* Copy packet, so we can recycle the original */
- memcpy(newskb->data, skb->data, urb->actual_length);
+ skb_copy_from_linear_data(skb, newskb->data, urb->actual_length);
/* Deliver this new skb */
dataskb = newskb;
/* And hook the old skb to the URB
@@ -921,7 +921,7 @@ static void irda_usb_receive(struct urb *urb)
/* Ask the networking layer to queue the packet for the IrDA stack */
dataskb->dev = self->netdev;
- dataskb->mac.raw = dataskb->data;
+ skb_reset_mac_header(dataskb);
dataskb->protocol = htons(ETH_P_IRDA);
len = dataskb->len;
netif_rx(dataskb);
diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c
index f0c61f3b2a82..0de867288a47 100644
--- a/drivers/net/irda/mcs7780.c
+++ b/drivers/net/irda/mcs7780.c
@@ -200,14 +200,14 @@ static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs)
/* Setup a communication between mcs7780 and agilent chip. */
static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs)
{
- IRDA_WARNING("This transceiver type is not supported yet.");
+ IRDA_WARNING("This transceiver type is not supported yet.\n");
return 1;
}
/* Setup a communication between mcs7780 and sharp chip. */
static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs)
{
- IRDA_WARNING("This transceiver type is not supported yet.");
+ IRDA_WARNING("This transceiver type is not supported yet.\n");
return 1;
}
@@ -279,7 +279,7 @@ static inline int mcs_setup_transceiver(struct mcs_cb *mcs)
break;
default:
- IRDA_WARNING("Unknown transceiver type: %d",
+ IRDA_WARNING("Unknown transceiver type: %d\n",
mcs->transceiver_type);
ret = 1;
}
@@ -318,7 +318,7 @@ static inline int mcs_setup_transceiver(struct mcs_cb *mcs)
return ret;
error:
- IRDA_ERROR("%s", msg);
+ IRDA_ERROR("%s\n", msg);
return ret;
}
@@ -353,7 +353,7 @@ static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf)
buf[0] = len & 0xff;
buf[1] = (len >> 8) & 0xff;
/* copy the data into the tx buffer. */
- memcpy(buf+2, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, buf + 2, skb->len);
/* put the fcs in the last four bytes in little endian order. */
buf[len - 4] = fcs & 0xff;
buf[len - 3] = (fcs >> 8) & 0xff;
@@ -377,7 +377,7 @@ static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf)
buf[0] = len & 0xff;
buf[1] = (len >> 8) & 0xff;
/* copy the data */
- memcpy(buf+2, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, buf + 2, skb->len);
/* put the fcs in last two bytes in little endian order. */
buf[len - 2] = fcs & 0xff;
buf[len - 1] = (fcs >> 8) & 0xff;
@@ -426,9 +426,9 @@ static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len)
}
skb_reserve(skb, 1);
- memcpy(skb->data, buf, new_len);
+ skb_copy_to_linear_data(skb, buf, new_len);
skb_put(skb, new_len);
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
skb->dev = mcs->netdev;
@@ -479,9 +479,9 @@ static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len)
}
skb_reserve(skb, 1);
- memcpy(skb->data, buf, new_len);
+ skb_copy_to_linear_data(skb, buf, new_len);
skb_put(skb, new_len);
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
skb->dev = mcs->netdev;
@@ -587,7 +587,7 @@ static int mcs_speed_change(struct mcs_cb *mcs)
} while(cnt++ < 100 && (rval & MCS_IRINTX));
if(cnt >= 100) {
- IRDA_ERROR("unable to change speed");
+ IRDA_ERROR("unable to change speed\n");
ret = -EIO;
goto error;
}
@@ -638,7 +638,7 @@ static int mcs_speed_change(struct mcs_cb *mcs)
default:
ret = 1;
- IRDA_WARNING("Unknown transceiver type: %d",
+ IRDA_WARNING("Unknown transceiver type: %d\n",
mcs->transceiver_type);
}
if (unlikely(ret))
@@ -733,7 +733,7 @@ static int mcs_net_open(struct net_device *netdev)
sprintf(hwname, "usb#%d", mcs->usbdev->devnum);
mcs->irlap = irlap_open(netdev, &mcs->qos, hwname);
if (!mcs->irlap) {
- IRDA_ERROR("mcs7780: irlap_open failed");
+ IRDA_ERROR("mcs7780: irlap_open failed\n");
goto error2;
}
@@ -862,7 +862,7 @@ static int mcs_hard_xmit(struct sk_buff *skb, struct net_device *ndev)
mcs->out_buf, wraplen, mcs_send_irq, mcs);
if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) {
- IRDA_ERROR("failed tx_urb: %d", ret);
+ IRDA_ERROR("failed tx_urb: %d\n", ret);
switch (ret) {
case -ENODEV:
case -EPIPE:
@@ -897,7 +897,7 @@ static int mcs_probe(struct usb_interface *intf,
if (!ndev)
goto error1;
- IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.", udev->devnum);
+ IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum);
/* what is it realy for? */
SET_MODULE_OWNER(ndev);
@@ -905,7 +905,7 @@ static int mcs_probe(struct usb_interface *intf,
ret = usb_reset_configuration(udev);
if (ret != 0) {
- IRDA_ERROR("mcs7780: usb reset configuration failed");
+ IRDA_ERROR("mcs7780: usb reset configuration failed\n");
goto error2;
}
@@ -950,7 +950,7 @@ static int mcs_probe(struct usb_interface *intf,
if (ret != 0)
goto error2;
- IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s",
+ IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s\n",
ndev->name);
mcs->transceiver_type = transceiver_type;
@@ -981,7 +981,7 @@ static void mcs_disconnect(struct usb_interface *intf)
free_netdev(mcs->netdev);
usb_set_intfdata(intf, NULL);
- IRDA_DEBUG(0, "MCS7780 now disconnected.");
+ IRDA_DEBUG(0, "MCS7780 now disconnected.\n");
}
/* Module insertion */
@@ -992,7 +992,7 @@ static int __init mcs_init(void)
/* register this driver with the USB subsystem */
result = usb_register(&mcs_driver);
if (result)
- IRDA_ERROR("usb_register failed. Error number %d", result);
+ IRDA_ERROR("usb_register failed. Error number %d\n", result);
return result;
}
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index 29b5ccd29d0b..d96c89751a71 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -1466,9 +1466,8 @@ static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
self->stats.tx_bytes += skb->len;
- memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data,
- skb->len);
-
+ skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start,
+ skb->len);
self->tx_fifo.len++;
self->tx_fifo.free++;
@@ -1869,10 +1868,14 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase)
/* Copy frame without CRC */
if (self->io.speed < 4000000) {
skb_put(skb, len-2);
- memcpy(skb->data, self->rx_buff.data, len-2);
+ skb_copy_to_linear_data(skb,
+ self->rx_buff.data,
+ len - 2);
} else {
skb_put(skb, len-4);
- memcpy(skb->data, self->rx_buff.data, len-4);
+ skb_copy_to_linear_data(skb,
+ self->rx_buff.data,
+ len - 4);
}
/* Move to next frame */
@@ -1881,7 +1884,7 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase)
self->stats.rx_packets++;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
self->netdev->last_rx = jiffies;
diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c
index 2272156af31e..fb196fd91855 100644
--- a/drivers/net/irda/pxaficp_ir.c
+++ b/drivers/net/irda/pxaficp_ir.c
@@ -386,12 +386,12 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, in
/* Align IP header to 20 bytes */
skb_reserve(skb, 1);
- memcpy(skb->data, si->dma_rx_buff, len);
+ skb_copy_to_linear_data(skb, si->dma_rx_buff, len);
skb_put(skb, len);
/* Feed it to IrLAP */
skb->dev = dev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
@@ -484,7 +484,7 @@ static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned long mtt = irda_get_mtt(skb);
si->dma_tx_buff_len = skb->len;
- memcpy(si->dma_tx_buff, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len);
if (mtt)
while ((unsigned)(OSCR - si->last_oscr)/4 < mtt)
diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c
index 937372d00398..056639f72bec 100644
--- a/drivers/net/irda/sa1100_ir.c
+++ b/drivers/net/irda/sa1100_ir.c
@@ -504,7 +504,7 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev
skb_put(skb, len);
skb->dev = dev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
si->stats.rx_packets++;
si->stats.rx_bytes += len;
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
index 31c623381ea8..198bf3bfa70f 100644
--- a/drivers/net/irda/smsc-ircc2.c
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -315,6 +315,7 @@ static struct smsc_chip __initdata lpc_chips_flat[] =
{
/* Base address 0x2E or 0x4E */
{ "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 },
+ { "47N227", KEY55_1|FIR|SERx4, 0x7a, 0x00 },
{ "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 },
{ NULL }
};
@@ -1161,7 +1162,7 @@ static int smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
self->new_speed = speed;
}
- memcpy(self->tx_buff.head, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, self->tx_buff.head, skb->len);
self->tx_buff.len = skb->len;
self->tx_buff.data = self->tx_buff.head;
@@ -1412,7 +1413,7 @@ static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self)
self->stats.rx_bytes += len;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
}
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
index 20d306fea4cb..755aa444a4dd 100644
--- a/drivers/net/irda/stir4200.c
+++ b/drivers/net/irda/stir4200.c
@@ -52,7 +52,6 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
#include <net/irda/irda_device.h>
#include <net/irda/wrapper.h>
#include <net/irda/crc.h>
@@ -349,7 +348,7 @@ static void fir_eof(struct stir_cb *stir)
}
skb_reserve(nskb, 1);
skb = nskb;
- memcpy(nskb->data, rx_buff->data, len);
+ skb_copy_to_linear_data(nskb, rx_buff->data, len);
} else {
nskb = dev_alloc_skb(rx_buff->truesize);
if (unlikely(!nskb)) {
@@ -364,7 +363,7 @@ static void fir_eof(struct stir_cb *stir)
skb_put(skb, len);
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
skb->dev = stir->netdev;
diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c
index c3ed9b3067e5..ff5358574d0a 100644
--- a/drivers/net/irda/via-ircc.c
+++ b/drivers/net/irda/via-ircc.c
@@ -925,8 +925,8 @@ static int via_ircc_hard_xmit_fir(struct sk_buff *skb,
self->tx_fifo.tail += skb->len;
self->stats.tx_bytes += skb->len;
- memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data,
- skb->len);
+ skb_copy_from_linear_data(skb,
+ self->tx_fifo.queue[self->tx_fifo.free].start, skb->len);
self->tx_fifo.len++;
self->tx_fifo.free++;
//F01 if (self->tx_fifo.len == 1) {
@@ -1125,7 +1125,7 @@ static int via_ircc_dma_receive_complete(struct via_ircc_cb *self,
self->stats.rx_bytes += len;
self->stats.rx_packets++;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
return TRUE;
@@ -1189,7 +1189,7 @@ F01_E */
skb_reserve(skb, 1);
skb_put(skb, len - 4);
- memcpy(skb->data, self->rx_buff.data, len - 4);
+ skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4);
IRDA_DEBUG(2, "%s(): len=%x.rx_buff=%p\n", __FUNCTION__,
len - 4, self->rx_buff.data);
@@ -1198,7 +1198,7 @@ F01_E */
self->stats.rx_bytes += len;
self->stats.rx_packets++;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
@@ -1234,7 +1234,7 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase)
}
skb_reserve(skb, 1);
skb_put(skb, len - 4 + 1);
- memcpy(skb->data, self->rx_buff.data, len - 4 + 1);
+ skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4 + 1);
st_fifo->tail++;
st_fifo->len++;
if (st_fifo->tail > MAX_RX_WINDOW)
@@ -1244,7 +1244,7 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase)
self->stats.rx_bytes += len;
self->stats.rx_packets++;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
if (st_fifo->len < (MAX_RX_WINDOW + 2)) {
@@ -1303,7 +1303,7 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase)
}
skb_reserve(skb, 1);
skb_put(skb, len - 4);
- memcpy(skb->data, self->rx_buff.data, len - 4);
+ skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4);
IRDA_DEBUG(2, "%s(): len=%x.head=%x\n", __FUNCTION__,
len - 4, st_fifo->head);
@@ -1313,7 +1313,7 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase)
self->stats.rx_bytes += len;
self->stats.rx_packets++;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
} //while
diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c
index 3457e9d8b667..c4be973867a6 100644
--- a/drivers/net/irda/vlsi_ir.c
+++ b/drivers/net/irda/vlsi_ir.c
@@ -595,7 +595,7 @@ static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd)
rd->skb = NULL;
skb->dev = ndev;
memcpy(skb_put(skb,len), rd->buf, len);
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
if (in_interrupt())
netif_rx(skb);
else
@@ -993,7 +993,7 @@ static int vlsi_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
goto drop;
}
else
- memcpy(rd->buf, skb->data, len);
+ skb_copy_from_linear_data(skb, rd->buf, len);
}
rd->skb = skb; /* remember skb for tx-complete stats */
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
index 4212657fa4f9..5182e800cc18 100644
--- a/drivers/net/irda/w83977af_ir.c
+++ b/drivers/net/irda/w83977af_ir.c
@@ -529,7 +529,7 @@ int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev)
/* Decide if we should use PIO or DMA transfer */
if (self->io.speed > PIO_MAX_SPEED) {
self->tx_buff.data = self->tx_buff.head;
- memcpy(self->tx_buff.data, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len);
self->tx_buff.len = skb->len;
mtt = irda_get_mtt(skb);
@@ -908,10 +908,14 @@ int w83977af_dma_receive_complete(struct w83977af_ir *self)
/* Copy frame without CRC */
if (self->io.speed < 4000000) {
skb_put(skb, len-2);
- memcpy(skb->data, self->rx_buff.data, len-2);
+ skb_copy_to_linear_data(skb,
+ self->rx_buff.data,
+ len - 2);
} else {
skb_put(skb, len-4);
- memcpy(skb->data, self->rx_buff.data, len-4);
+ skb_copy_to_linear_data(skb,
+ self->rx_buff.data,
+ len - 4);
}
/* Move to next frame */
@@ -919,7 +923,7 @@ int w83977af_dma_receive_complete(struct w83977af_ir *self)
self->stats.rx_packets++;
skb->dev = self->netdev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
self->netdev->last_rx = jiffies;
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c
index 0e9ba3c3faf7..347d50cd77d4 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/iseries_veth.c
@@ -1540,7 +1540,6 @@ static void veth_receive(struct veth_lpar_connection *cnx,
}
skb_put(skb, length);
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb); /* send it up */
diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
index afc2ec72529e..dfde80e54aef 100644
--- a/drivers/net/ixgb/ixgb_main.c
+++ b/drivers/net/ixgb/ixgb_main.c
@@ -1182,24 +1182,27 @@ ixgb_tso(struct ixgb_adapter *adapter, struct sk_buff *skb)
if (likely(skb_is_gso(skb))) {
struct ixgb_buffer *buffer_info;
+ struct iphdr *iph;
+
if (skb_header_cloned(skb)) {
err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
if (err)
return err;
}
- hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
mss = skb_shinfo(skb)->gso_size;
- skb->nh.iph->tot_len = 0;
- skb->nh.iph->check = 0;
- skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr,
- skb->nh.iph->daddr,
- 0, IPPROTO_TCP, 0);
- ipcss = skb->nh.raw - skb->data;
- ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data;
- ipcse = skb->h.raw - skb->data - 1;
- tucss = skb->h.raw - skb->data;
- tucso = (void *)&(skb->h.th->check) - (void *)skb->data;
+ iph = ip_hdr(skb);
+ iph->tot_len = 0;
+ iph->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0,
+ IPPROTO_TCP, 0);
+ ipcss = skb_network_offset(skb);
+ ipcso = (void *)&(iph->check) - (void *)skb->data;
+ ipcse = skb_transport_offset(skb) - 1;
+ tucss = skb_transport_offset(skb);
+ tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data;
tucse = 0;
i = adapter->tx_ring.next_to_use;
@@ -1243,7 +1246,7 @@ ixgb_tx_csum(struct ixgb_adapter *adapter, struct sk_buff *skb)
if(likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
struct ixgb_buffer *buffer_info;
- css = skb->h.raw - skb->data;
+ css = skb_transport_offset(skb);
cso = css + skb->csum_offset;
i = adapter->tx_ring.next_to_use;
@@ -2014,9 +2017,12 @@ ixgb_clean_rx_irq(struct ixgb_adapter *adapter)
netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
if (new_skb) {
skb_reserve(new_skb, NET_IP_ALIGN);
- memcpy(new_skb->data - NET_IP_ALIGN,
- skb->data - NET_IP_ALIGN,
- length + NET_IP_ALIGN);
+ skb_copy_to_linear_data_offset(new_skb,
+ -NET_IP_ALIGN,
+ (skb->data -
+ NET_IP_ALIGN),
+ (length +
+ NET_IP_ALIGN));
/* save the skb in buffer_info as good */
buffer_info->skb = skb;
skb = new_skb;
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c
index a4eccb11d677..6683afc02aaa 100644
--- a/drivers/net/ixp2000/ixpdev.c
+++ b/drivers/net/ixp2000/ixpdev.c
@@ -110,11 +110,10 @@ static int ixpdev_rx(struct net_device *dev, int *budget)
skb = dev_alloc_skb(desc->pkt_length + 2);
if (likely(skb != NULL)) {
- skb->dev = nds[desc->channel];
skb_reserve(skb, 2);
eth_copy_and_sum(skb, buf, desc->pkt_length, 0);
skb_put(skb, desc->pkt_length);
- skb->protocol = eth_type_trans(skb, skb->dev);
+ skb->protocol = eth_type_trans(skb, nds[desc->channel]);
skb->dev->last_rx = jiffies;
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index a3843320dbe1..0fe96c85828b 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -988,7 +988,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (lance_debug > 5)
printk("%s: bouncing a high-memory packet (%#x).\n",
dev->name, (u32)isa_virt_to_bus(skb->data));
- memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len);
+ skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len);
lp->tx_ring[entry].base =
((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
dev_kfree_skb(skb);
@@ -1184,7 +1184,6 @@ lance_rx(struct net_device *dev)
}
break;
}
- skb->dev = dev;
skb_reserve(skb,2); /* 16 byte align */
skb_put(skb,pkt_len); /* Make room */
eth_copy_and_sum(skb,
diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c
index 452863d5d498..0edcd125fd61 100644
--- a/drivers/net/lasi_82596.c
+++ b/drivers/net/lasi_82596.c
@@ -801,7 +801,6 @@ memory_squeeze:
lp->stats.rx_dropped++;
}
else {
- skb->dev = dev;
if (!rx_in_place) {
/* 16 byte align the data fields */
dma_sync_single_for_cpu(lp->dev, (dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE);
diff --git a/drivers/net/lib8390.c b/drivers/net/lib8390.c
index e726c06b8dc6..5c86e737f954 100644
--- a/drivers/net/lib8390.c
+++ b/drivers/net/lib8390.c
@@ -722,7 +722,6 @@ static void ei_receive(struct net_device *dev)
else
{
skb_reserve(skb,2); /* IP headers on 16 byte boundaries */
- skb->dev = dev;
skb_put(skb, pkt_len); /* Make room */
ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
skb->protocol=eth_type_trans(skb,dev);
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index 2b739fd584f1..6ba6ed2b480a 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -75,8 +75,9 @@ static DEFINE_PER_CPU(struct pcpu_lstats, pcpu_lstats);
#ifdef LOOPBACK_TSO
static void emulate_large_send_offload(struct sk_buff *skb)
{
- struct iphdr *iph = skb->nh.iph;
- struct tcphdr *th = (struct tcphdr*)(skb->nh.raw + (iph->ihl * 4));
+ struct iphdr *iph = ip_hdr(skb);
+ struct tcphdr *th = (struct tcphdr *)(skb_network_header(skb) +
+ (iph->ihl * 4));
unsigned int doffset = (iph->ihl + th->doff) * 4;
unsigned int mtu = skb_shinfo(skb)->gso_size + doffset;
unsigned int offset = 0;
@@ -90,10 +91,11 @@ static void emulate_large_send_offload(struct sk_buff *skb)
if (!nskb)
break;
skb_reserve(nskb, 32);
- nskb->mac.raw = nskb->data - 14;
- nskb->nh.raw = nskb->data;
- iph = nskb->nh.iph;
- memcpy(nskb->data, skb->nh.raw, doffset);
+ skb_set_mac_header(nskb, -ETH_HLEN);
+ skb_reset_network_header(nskb);
+ iph = ip_hdr(nskb);
+ skb_copy_to_linear_data(nskb, skb_network_header(skb),
+ doffset);
if (skb_copy_bits(skb,
doffset + offset,
nskb->data + doffset,
@@ -108,7 +110,7 @@ static void emulate_large_send_offload(struct sk_buff *skb)
memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
nskb->pkt_type = skb->pkt_type;
- th = (struct tcphdr*)(nskb->nh.raw + iph->ihl*4);
+ th = (struct tcphdr *)(skb_network_header(nskb) + iph->ihl * 4);
iph->tot_len = htons(frag_size + doffset);
iph->id = htons(id);
iph->check = 0;
@@ -137,7 +139,6 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
skb_orphan(skb);
skb->protocol = eth_type_trans(skb,dev);
- skb->dev = dev;
#ifndef LOOPBACK_MUST_CHECKSUM
skb->ip_summed = CHECKSUM_UNNECESSARY;
#endif
@@ -145,7 +146,7 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
#ifdef LOOPBACK_TSO
if (skb_is_gso(skb)) {
BUG_ON(skb->protocol != htons(ETH_P_IP));
- BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP);
+ BUG_ON(ip_hdr(skb)->protocol != IPPROTO_TCP);
emulate_large_send_offload(skb);
return 0;
@@ -163,11 +164,9 @@ 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 = &loopback_stats;
+ struct net_device_stats *stats = &dev->stats;
unsigned long bytes = 0;
unsigned long packets = 0;
int i;
@@ -207,7 +206,6 @@ static const struct ethtool_ops loopback_ethtool_ops = {
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,
diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c
index 177c502f7385..5fc18da1873d 100644
--- a/drivers/net/lp486e.c
+++ b/drivers/net/lp486e.c
@@ -676,7 +676,6 @@ i596_rx_one(struct net_device *dev, struct i596_private *lp,
return 1;
}
- skb->dev = dev;
memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len);
skb->protocol = eth_type_trans(skb,dev);
diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c
index e960138011c0..90e695d53266 100644
--- a/drivers/net/mac89x0.c
+++ b/drivers/net/mac89x0.c
@@ -530,7 +530,6 @@ net_rx(struct net_device *dev)
return;
}
skb_put(skb, length);
- skb->dev = dev;
memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length);
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 2e9571bf0736..0e04f7ac3f2e 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -357,7 +357,6 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
}
skb_reserve(skb, RX_OFFSET);
- skb->dev = bp->dev;
skb->ip_summed = CHECKSUM_NONE;
skb_put(skb, len);
@@ -368,9 +367,10 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
BUG_ON(frag != last_frag);
frag_len = len - offset;
}
- memcpy(skb->data + offset,
- bp->rx_buffers + (RX_BUFFER_SIZE * frag),
- frag_len);
+ skb_copy_to_linear_data_offset(skb, offset,
+ (bp->rx_buffers +
+ (RX_BUFFER_SIZE * frag)),
+ frag_len);
offset += RX_BUFFER_SIZE;
bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED);
wmb();
@@ -576,7 +576,8 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
int i;
dev_dbg(&bp->pdev->dev,
"start_xmit: len %u head %p data %p tail %p end %p\n",
- skb->len, skb->head, skb->data, skb->tail, skb->end);
+ skb->len, skb->head, skb->data,
+ skb_tail_pointer(skb), skb_end_pointer(skb));
dev_dbg(&bp->pdev->dev,
"data:");
for (i = 0; i < 16; i++)
diff --git a/drivers/net/mace.c b/drivers/net/mace.c
index 9ec24f0d5d68..b3bd62394958 100644
--- a/drivers/net/mace.c
+++ b/drivers/net/mace.c
@@ -939,7 +939,6 @@ static irqreturn_t mace_rxdma_intr(int irq, void *dev_id)
else /* Ethernet header; mace includes FCS */
nb -= 8;
skb_put(skb, nb);
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
mp->stats.rx_bytes += skb->len;
netif_rx(skb);
diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c
index 5d541e873041..27911c07558d 100644
--- a/drivers/net/macmace.c
+++ b/drivers/net/macmace.c
@@ -420,8 +420,7 @@ static int mace_xmit_start(struct sk_buff *skb, struct net_device *dev)
mp->stats.tx_bytes += skb->len;
/* We need to copy into our xmit buffer to take care of alignment and caching issues */
-
- memcpy((void *) mp->tx_ring, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, mp->tx_ring, skb->len);
/* load the Tx DMA and fire it off */
@@ -621,7 +620,6 @@ static void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf)
skb_reserve(skb,2);
memcpy(skb_put(skb, mf->len), mf->data, mf->len);
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->last_rx = jiffies;
diff --git a/drivers/net/meth.c b/drivers/net/meth.c
index 7e69ca6edd91..0343ea12b299 100644
--- a/drivers/net/meth.c
+++ b/drivers/net/meth.c
@@ -421,7 +421,6 @@ static void meth_rx(struct net_device* dev, unsigned long int_status)
/* Write metadata, and then pass to the receive level */
skb_put(skb_c, len);
priv->rx_skbs[priv->rx_write] = skb;
- skb_c->dev = dev;
skb_c->protocol = eth_type_trans(skb_c, dev);
dev->last_rx = jiffies;
priv->stats.rx_packets++;
@@ -609,7 +608,7 @@ static void meth_tx_short_prepare(struct meth_private *priv,
desc->header.raw = METH_TX_CMD_INT_EN | (len-1) | ((128-len) << 16);
/* maybe I should set whole thing to 0 first... */
- memcpy(desc->data.dt + (120 - len), skb->data, skb->len);
+ skb_copy_from_linear_data(skb, desc->data.dt + (120 - len), skb->len);
if (skb->len < len)
memset(desc->data.dt + 120 - len + skb->len, 0, len-skb->len);
}
@@ -627,8 +626,8 @@ static void meth_tx_1page_prepare(struct meth_private *priv,
/* unaligned part */
if (unaligned_len) {
- memcpy(desc->data.dt + (120 - unaligned_len),
- skb->data, unaligned_len);
+ skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len),
+ unaligned_len);
desc->header.raw |= (128 - unaligned_len) << 16;
}
@@ -653,8 +652,8 @@ static void meth_tx_2page_prepare(struct meth_private *priv,
desc->header.raw = METH_TX_CMD_INT_EN | TX_CATBUF1 | TX_CATBUF2| (skb->len - 1);
/* unaligned part */
if (unaligned_len){
- memcpy(desc->data.dt + (120 - unaligned_len),
- skb->data, unaligned_len);
+ skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len),
+ unaligned_len);
desc->header.raw |= (128 - unaligned_len) << 16;
}
diff --git a/drivers/net/mipsnet.c b/drivers/net/mipsnet.c
index f42b9e201937..403f63afd201 100644
--- a/drivers/net/mipsnet.c
+++ b/drivers/net/mipsnet.c
@@ -101,7 +101,6 @@ static inline ssize_t mipsnet_get_fromdev(struct net_device *dev, size_t count)
if (ioiocpy_frommipsnet(dev, skb_put(skb, len), len))
return -EFAULT;
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index 8015a7c5b0c9..ab15ecd4b3d6 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -434,7 +434,6 @@ static int mv643xx_eth_receive_queue(struct net_device *dev, int budget)
* received packet
*/
skb_put(skb, pkt_info.byte_cnt - 4);
- skb->dev = dev;
if (pkt_info.cmd_sts & ETH_LAYER_4_CHECKSUM_OK) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -1162,15 +1161,15 @@ static void eth_tx_submit_descs_for_skb(struct mv643xx_private *mp,
cmd_sts |= ETH_GEN_TCP_UDP_CHECKSUM |
ETH_GEN_IP_V_4_CHECKSUM |
- skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
+ ip_hdr(skb)->ihl << ETH_TX_IHL_SHIFT;
- switch (skb->nh.iph->protocol) {
+ switch (ip_hdr(skb)->protocol) {
case IPPROTO_UDP:
cmd_sts |= ETH_UDP_FRAME;
- desc->l4i_chk = skb->h.uh->check;
+ desc->l4i_chk = udp_hdr(skb)->check;
break;
case IPPROTO_TCP:
- desc->l4i_chk = skb->h.th->check;
+ desc->l4i_chk = tcp_hdr(skb)->check;
break;
default:
BUG();
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c
index f8efe0e70a6b..16e3c4315e82 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/myri10ge/myri10ge.c
@@ -879,7 +879,7 @@ myri10ge_rx_skb_build(struct sk_buff *skb, u8 * va,
* skb_pull() (for ether_pad and eth_type_trans()) requires
* the beginning of the packet in skb_headlen(), move it
* manually */
- memcpy(skb->data, va, hlen);
+ skb_copy_to_linear_data(skb, va, hlen);
skb_shinfo(skb)->frags[0].page_offset += hlen;
skb_shinfo(skb)->frags[0].size -= hlen;
skb->data_len -= hlen;
@@ -1020,7 +1020,6 @@ myri10ge_rx_done(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
skb_shinfo(skb)->nr_frags = 0;
}
skb->protocol = eth_type_trans(skb, dev);
- skb->dev = dev;
if (mgp->csum_flag) {
if ((skb->protocol == htons(ETH_P_IP)) ||
@@ -2030,7 +2029,7 @@ again:
odd_flag = 0;
flags = (MXGEFW_FLAGS_NO_TSO | MXGEFW_FLAGS_FIRST);
if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
- cksum_offset = (skb->h.raw - skb->data);
+ cksum_offset = skb_transport_offset(skb);
pseudo_hdr_offset = cksum_offset + skb->csum_offset;
/* If the headers are excessively large, then we must
* fall back to a software checksum */
@@ -2055,7 +2054,7 @@ again:
* send loop that we are still in the
* header portion of the TSO packet.
* TSO header must be at most 134 bytes long */
- cum_len = -((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+ cum_len = -(skb_transport_offset(skb) + tcp_hdrlen(skb));
/* for TSO, pseudo_hdr_offset holds mss.
* The firmware figures out where to put
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index ee26ef52289f..13444da93273 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -368,7 +368,7 @@ static __be16 myri_type_trans(struct sk_buff *skb, struct net_device *dev)
struct ethhdr *eth;
unsigned char *rawp;
- skb->mac.raw = (((unsigned char *)skb->data) + MYRI_PAD_LEN);
+ skb_set_mac_header(skb, MYRI_PAD_LEN);
skb_pull(skb, dev->hard_header_len);
eth = eth_hdr(skb);
@@ -502,7 +502,7 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev)
copy_skb->dev = dev;
DRX(("resv_and_put "));
skb_put(copy_skb, len);
- memcpy(copy_skb->data, skb->data, len);
+ skb_copy_from_linear_data(skb, copy_skb->data, len);
/* Reuse original ring buffer. */
DRX(("reuse "));
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 349b96a3ec4c..a8d7ff2c96ac 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -2289,7 +2289,6 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
* without copying to a minimally-sized skbuff. */
if (pkt_len < rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
- skb->dev = dev;
/* 16 byte align the IP header */
skb_reserve(skb, RX_OFFSET);
pci_dma_sync_single_for_cpu(np->pci_dev,
diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c
index a53644f6a29b..2b8da0a54998 100644
--- a/drivers/net/netx-eth.c
+++ b/drivers/net/netx-eth.c
@@ -168,7 +168,6 @@ static void netx_eth_receive(struct net_device *ndev)
FIFO_PTR_SEGMENT(seg) | FIFO_PTR_FRAMENO(frameno));
ndev->last_rx = jiffies;
- skb->dev = ndev;
skb->protocol = eth_type_trans(skb, ndev);
netif_rx(skb);
priv->stats.rx_packets++;
diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c
index 6537574a9cda..0fba8f190762 100644
--- a/drivers/net/netxen/netxen_nic_hw.c
+++ b/drivers/net/netxen/netxen_nic_hw.c
@@ -35,6 +35,8 @@
#include "netxen_nic_hw.h"
#include "netxen_nic_phan_reg.h"
+#include <net/ip.h>
+
/* PCI Windowing for DDR regions. */
#define ADDR_IN_RANGE(addr, low, high) \
@@ -371,22 +373,21 @@ void netxen_tso_check(struct netxen_adapter *adapter,
struct cmd_desc_type0 *desc, struct sk_buff *skb)
{
if (desc->mss) {
- desc->total_hdr_length = sizeof(struct ethhdr) +
- ((skb->nh.iph)->ihl * sizeof(u32)) +
- ((skb->h.th)->doff * sizeof(u32));
+ desc->total_hdr_length = (sizeof(struct ethhdr) +
+ ip_hdrlen(skb) + tcp_hdrlen(skb));
netxen_set_cmd_desc_opcode(desc, TX_TCP_LSO);
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
- if (skb->nh.iph->protocol == IPPROTO_TCP) {
+ if (ip_hdr(skb)->protocol == IPPROTO_TCP) {
netxen_set_cmd_desc_opcode(desc, TX_TCP_PKT);
- } else if (skb->nh.iph->protocol == IPPROTO_UDP) {
+ } else if (ip_hdr(skb)->protocol == IPPROTO_UDP) {
netxen_set_cmd_desc_opcode(desc, TX_UDP_PKT);
} else {
return;
}
}
adapter->stats.xmitcsummed++;
- desc->tcp_hdr_offset = skb->h.raw - skb->data;
- desc->ip_hdr_offset = skb->nh.raw - skb->data;
+ desc->tcp_hdr_offset = skb_transport_offset(skb);
+ desc->ip_hdr_offset = skb_network_offset(skb);
}
int netxen_is_flash_supported(struct netxen_adapter *adapter)
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index eff965dc5fff..5cd40562da7c 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -1129,7 +1129,6 @@ netxen_process_rcv(struct netxen_adapter *adapter, int ctxid,
port->stats.csummed++;
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
- skb->dev = netdev;
if (desc_ctx == RCV_DESC_LRO_CTXID) {
/* True length was only available on the last pkt */
skb_put(skb, buffer->lro_length);
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 7d2525e76abb..ab25c225a07e 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -41,6 +41,7 @@
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
+#include <net/ip.h>
MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver");
MODULE_LICENSE("GPL");
@@ -778,9 +779,8 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
if (skb_shinfo(skb)->gso_size > 0) {
no_of_desc++;
- if (((skb->nh.iph)->ihl * sizeof(u32)) +
- ((skb->h.th)->doff * sizeof(u32)) +
- sizeof(struct ethhdr) >
+ if ((ip_hdrlen(skb) + tcp_hdrlen(skb) +
+ sizeof(struct ethhdr)) >
(sizeof(struct cmd_desc_type0) - 2)) {
no_of_desc++;
}
@@ -920,8 +920,10 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
/* copy the next 64 bytes - should be enough except
* for pathological case
*/
- memcpy((void *)hwdesc, (void *)(skb->data) +
- first_hdr_len, hdr_len - first_hdr_len);
+ skb_copy_from_linear_data_offset(skb, first_hdr_len,
+ hwdesc,
+ (hdr_len -
+ first_hdr_len));
producer = get_next_index(producer, max_tx_desc_count);
}
}
diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c
index 8be0d030d6f4..3d5b4232f65f 100644
--- a/drivers/net/ni5010.c
+++ b/drivers/net/ni5010.c
@@ -562,7 +562,6 @@ static void ni5010_rx(struct net_device *dev)
return;
}
- skb->dev = dev;
skb_reserve(skb, 2);
/* Read packet into buffer */
diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c
index a6f4b24b0176..8dbd6d1900b5 100644
--- a/drivers/net/ni52.c
+++ b/drivers/net/ni52.c
@@ -934,7 +934,6 @@ static void ni52_rcv_int(struct net_device *dev)
skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
if(skb != NULL)
{
- skb->dev = dev;
skb_reserve(skb,2);
skb_put(skb,totlen);
eth_copy_and_sum(skb,(char *) p->base+(unsigned long) rbd->buffer,totlen,0);
@@ -1183,7 +1182,7 @@ static int ni52_send_packet(struct sk_buff *skb, struct net_device *dev)
else
#endif
{
- memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
+ skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len);
len = skb->len;
if (len < ETH_ZLEN) {
len = ETH_ZLEN;
diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c
index 1578f4d98498..3818edf0ac18 100644
--- a/drivers/net/ni65.c
+++ b/drivers/net/ni65.c
@@ -610,7 +610,6 @@ static void *ni65_alloc_mem(struct net_device *dev,char *what,int size,int type)
printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what);
return NULL;
}
- skb->dev = dev;
skb_reserve(skb,2+16);
skb_put(skb,R_BUF_SIZE); /* grab the whole space .. (not necessary) */
ptr = skb->data;
@@ -1094,7 +1093,6 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
if(skb)
{
skb_reserve(skb,2);
- skb->dev = dev;
#ifdef RCV_VIA_SKB
if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) {
skb_put(skb,len);
@@ -1178,8 +1176,9 @@ static int ni65_send_packet(struct sk_buff *skb, struct net_device *dev)
if( (unsigned long) (skb->data + skb->len) > 0x1000000) {
#endif
- memcpy((char *) p->tmdbounce[p->tmdbouncenum] ,(char *)skb->data,
- (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len);
+ skb_copy_from_linear_data(skb, p->tmdbounce[p->tmdbouncenum],
+ skb->len > T_BUF_SIZE ? T_BUF_SIZE :
+ skb->len);
if (len > skb->len)
memset((char *)p->tmdbounce[p->tmdbouncenum]+skb->len, 0, len-skb->len);
dev_kfree_skb (skb);
diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c
index 9ec6e9e54f47..6a32338623f1 100644
--- a/drivers/net/ns83820.c
+++ b/drivers/net/ns83820.c
@@ -607,7 +607,6 @@ static inline int rx_refill(struct net_device *ndev, gfp_t gfp)
res &= 0xf;
skb_reserve(skb, res);
- skb->dev = ndev;
if (gfp != GFP_ATOMIC)
spin_lock_irqsave(&dev->rx_info.lock, flags);
res = ns83820_add_rx_skb(dev, skb);
@@ -1157,9 +1156,9 @@ again:
extsts = 0;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
extsts |= EXTSTS_IPPKT;
- if (IPPROTO_TCP == skb->nh.iph->protocol)
+ if (IPPROTO_TCP == ip_hdr(skb)->protocol)
extsts |= EXTSTS_TCPPKT;
- else if (IPPROTO_UDP == skb->nh.iph->protocol)
+ else if (IPPROTO_UDP == ip_hdr(skb)->protocol)
extsts |= EXTSTS_UDPPKT;
}
diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c
index d670ac74824f..76fe9dd8e841 100644
--- a/drivers/net/pasemi_mac.c
+++ b/drivers/net/pasemi_mac.c
@@ -334,8 +334,6 @@ static void pasemi_mac_replenish_rx_ring(struct net_device *dev)
break;
}
- skb->dev = dev;
-
dma = pci_map_single(mac->dma_pdev, skb->data, skb->len,
PCI_DMA_FROMDEVICE);
@@ -731,16 +729,18 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev)
dflags = XCT_MACTX_O | XCT_MACTX_ST | XCT_MACTX_SS | XCT_MACTX_CRC_PAD;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- switch (skb->nh.iph->protocol) {
+ const unsigned char *nh = skb_network_header(skb);
+
+ switch (ip_hdr(skb)->protocol) {
case IPPROTO_TCP:
dflags |= XCT_MACTX_CSUM_TCP;
- dflags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2);
- dflags |= XCT_MACTX_IPO(skb->nh.raw - skb->data);
+ dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2);
+ dflags |= XCT_MACTX_IPO(nh - skb->data);
break;
case IPPROTO_UDP:
dflags |= XCT_MACTX_CSUM_UDP;
- dflags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2);
- dflags |= XCT_MACTX_IPO(skb->nh.raw - skb->data);
+ dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2);
+ dflags |= XCT_MACTX_IPO(nh - skb->data);
break;
}
}
diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c
index 6ca4e4fa6b88..df8998b4f37e 100644
--- a/drivers/net/pci-skeleton.c
+++ b/drivers/net/pci-skeleton.c
@@ -1344,7 +1344,7 @@ static int netdrv_start_xmit (struct sk_buff *skb, struct net_device *dev)
tp->tx_info[entry].skb = skb;
/* tp->tx_info[entry].mapping = 0; */
- memcpy (tp->tx_buf[entry], skb->data, skb->len);
+ skb_copy_from_linear_data(skb, tp->tx_buf[entry], skb->len);
/* Note: the chip doesn't have auto-pad! */
NETDRV_W32 (TxStatus0 + (entry * sizeof(u32)),
@@ -1565,7 +1565,6 @@ static void netdrv_rx_interrupt (struct net_device *dev,
skb = dev_alloc_skb (pkt_size + 2);
if (skb) {
- skb->dev = dev;
skb_reserve (skb, 2); /* 16 byte align the IP fields. */
eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index c7bd9c1c7f31..2b395ee21f75 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -1056,7 +1056,6 @@ static int el3_rx(struct net_device *dev, int worklimit)
DEBUG(3, " Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2);
insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
((pkt_len+3)>>2));
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
index 461e8274ef69..143ae2ff309e 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -883,7 +883,6 @@ static int el3_rx(struct net_device *dev)
DEBUG(3, " Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2);
insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
(pkt_len+3)>>2);
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index 6139048f8117..808fae1577e0 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -1136,7 +1136,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
ei_block_output(dev, length, skb->data, output_page);
else {
memset(packet, 0, ETH_ZLEN);
- memcpy(packet, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, packet, skb->len);
ei_block_output(dev, length, packet, output_page);
}
@@ -1496,7 +1496,6 @@ static void ei_receive(struct net_device *dev)
else
{
skb_reserve(skb,2); /* IP headers on 16 byte boundaries */
- skb->dev = dev;
skb_put(skb, pkt_len); /* Make room */
ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
skb->protocol=eth_type_trans(skb,dev);
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
index 0d7de617e535..3f93d4933235 100644
--- a/drivers/net/pcmcia/fmvj18x_cs.c
+++ b/drivers/net/pcmcia/fmvj18x_cs.c
@@ -999,7 +999,6 @@ static void fjn_rx(struct net_device *dev)
lp->stats.rx_dropped++;
break;
}
- skb->dev = dev;
skb_reserve(skb, 2);
insw(ioaddr + DATAPORT, skb_put(skb, pkt_len),
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
index 3b707747a811..73da611fd536 100644
--- a/drivers/net/pcmcia/nmclan_cs.c
+++ b/drivers/net/pcmcia/nmclan_cs.c
@@ -1182,12 +1182,10 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt)
skb = dev_alloc_skb(pkt_len+2);
if (skb != NULL) {
- skb->dev = dev;
-
skb_reserve(skb, 2);
insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
if (pkt_len & 1)
- *(skb->tail-1) = inb(ioaddr + AM2150_RCV);
+ *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index 2561f76033ea..7912dbd14251 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -1669,7 +1669,6 @@ static void smc_rx(struct net_device *dev)
(packet_length+1)>>1);
skb->protocol = eth_type_trans(skb, dev);
- skb->dev = dev;
netif_rx(skb);
dev->last_rx = jiffies;
smc->stats.rx_packets++;
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index 5879e7c36988..809ec440b8eb 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -1226,7 +1226,6 @@ xirc2ps_interrupt(int irq, void *dev_id)
(pktlen+1)>>1);
}
skb->protocol = eth_type_trans(skb, dev);
- skb->dev = dev;
netif_rx(skb);
dev->last_rx = jiffies;
lp->stats.rx_packets++;
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 4d94ba7899bf..0791360a6a66 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1206,7 +1206,6 @@ static void pcnet32_rx_entry(struct net_device *dev,
PCI_DMA_FROMDEVICE);
skb_put(skb, pkt_len);
lp->rx_skbuff[entry] = newskb;
- newskb->dev = dev;
lp->rx_dma_addr[entry] =
pci_map_single(lp->pci_dev,
newskb->data,
diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c
index 66da91bb1388..68c99b4c5255 100644
--- a/drivers/net/phy/fixed.c
+++ b/drivers/net/phy/fixed.c
@@ -276,21 +276,15 @@ static int fixed_mdio_register_device(int number, int speed, int duplex)
artificially, we are binding the driver here by hand;
it will be the same for all the fixed phys anyway.
*/
- down_write(&phydev->dev.bus->subsys.rwsem);
-
phydev->dev.driver = &fixed_mdio_driver.driver;
err = phydev->dev.driver->probe(&phydev->dev);
if(err < 0) {
printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id);
- up_write(&phydev->dev.bus->subsys.rwsem);
goto probe_fail;
}
err = device_bind_driver(&phydev->dev);
-
- up_write(&phydev->dev.bus->subsys.rwsem);
-
if (err)
goto probe_fail;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 7d5b6d1838c8..8f01952c4850 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -208,16 +208,12 @@ struct phy_device *phy_attach(struct net_device *dev,
* exist, and we should use the genphy driver. */
if (NULL == d->driver) {
int err;
- down_write(&d->bus->subsys.rwsem);
d->driver = &genphy_driver.driver;
err = d->driver->probe(d);
-
if (err >= 0)
err = device_bind_driver(d);
- up_write(&d->bus->subsys.rwsem);
-
if (err)
return ERR_PTR(err);
}
@@ -258,11 +254,8 @@ void phy_detach(struct phy_device *phydev)
* was using the generic driver), we unbind the device
* from the generic driver so that there's a chance a
* real driver could be loaded */
- if (phydev->dev.driver == &genphy_driver.driver) {
- down_write(&phydev->dev.bus->subsys.rwsem);
+ if (phydev->dev.driver == &genphy_driver.driver)
device_release_driver(&phydev->dev);
- up_write(&phydev->dev.bus->subsys.rwsem);
- }
}
EXPORT_SYMBOL(phy_detach);
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index 6bb085f54437..8754cf3356b0 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -546,7 +546,7 @@ static __be16 plip_type_trans(struct sk_buff *skb, struct net_device *dev)
struct ethhdr *eth;
unsigned char *rawp;
- skb->mac.raw=skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb,dev->hard_header_len);
eth = eth_hdr(skb);
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 933e2f3c77aa..caabbc408c34 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -802,9 +802,9 @@ process_input_packet(struct asyncppp *ap)
/* check for address/control and protocol compression */
p = skb->data;
- if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
+ if (p[0] == PPP_ALLSTATIONS) {
/* chop off address/control */
- if (skb->len < 3)
+ if (p[1] != PPP_UI || skb->len < 3)
goto err;
p = skb_pull(skb, 2);
}
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index ef58e4128782..6d596ca50cfd 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -88,8 +88,6 @@ struct ppp_file {
#define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp)
#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel)
-#define ROUNDUP(n, x) (((n) + (x) - 1) / (x))
-
/*
* Data structure describing one ppp unit.
* A ppp unit corresponds to a ppp network interface device
@@ -1297,7 +1295,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
*/
fragsize = len;
if (nfree > 1)
- fragsize = ROUNDUP(fragsize, nfree);
+ fragsize = DIV_ROUND_UP(fragsize, nfree);
/* nbigger channels get fragsize bytes, the rest get fragsize-1,
except if nbigger==0, then they all get fragsize. */
nbigger = len % nfree;
@@ -1685,7 +1683,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
skb_pull_rcsum(skb, 2);
skb->dev = ppp->dev;
skb->protocol = htons(npindex_to_ethertype[npi]);
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
netif_rx(skb);
ppp->dev->last_rx = jiffies;
}
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
index b6f0e9a25e26..5918fab38349 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp_synctty.c
@@ -594,7 +594,8 @@ ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
return NULL;
}
skb_reserve(npkt,2);
- memcpy(skb_put(npkt,skb->len), skb->data, skb->len);
+ skb_copy_from_linear_data(skb,
+ skb_put(npkt, skb->len), skb->len);
kfree_skb(skb);
skb = npkt;
}
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index ebfa2967cd68..6f98834e6ace 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -207,7 +207,7 @@ static inline struct pppox_sock *get_item(unsigned long sid,
static inline struct pppox_sock *get_item_by_addr(struct sockaddr_pppox *sp)
{
- struct net_device *dev = NULL;
+ struct net_device *dev;
int ifindex;
dev = dev_get_by_name(sp->sa_addr.pppoe.dev);
@@ -218,20 +218,6 @@ static inline struct pppox_sock *get_item_by_addr(struct sockaddr_pppox *sp)
return get_item(sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex);
}
-static inline int set_item(struct pppox_sock *po)
-{
- int i;
-
- if (!po)
- return -EINVAL;
-
- write_lock_bh(&pppoe_hash_lock);
- i = __set_item(po);
- write_unlock_bh(&pppoe_hash_lock);
-
- return i;
-}
-
static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int ifindex)
{
struct pppox_sock *ret;
@@ -255,54 +241,53 @@ static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int
static void pppoe_flush_dev(struct net_device *dev)
{
int hash;
-
BUG_ON(dev == NULL);
- read_lock_bh(&pppoe_hash_lock);
+ write_lock_bh(&pppoe_hash_lock);
for (hash = 0; hash < PPPOE_HASH_SIZE; hash++) {
struct pppox_sock *po = item_hash_table[hash];
while (po != NULL) {
- if (po->pppoe_dev == dev) {
- struct sock *sk = sk_pppox(po);
-
- sock_hold(sk);
- po->pppoe_dev = NULL;
+ struct sock *sk = sk_pppox(po);
+ if (po->pppoe_dev != dev) {
+ po = po->next;
+ continue;
+ }
+ po->pppoe_dev = NULL;
+ dev_put(dev);
- /* We hold a reference to SK, now drop the
- * hash table lock so that we may attempt
- * to lock the socket (which can sleep).
- */
- read_unlock_bh(&pppoe_hash_lock);
- lock_sock(sk);
+ /* We always grab the socket lock, followed by the
+ * pppoe_hash_lock, in that order. Since we should
+ * hold the sock lock while doing any unbinding,
+ * we need to release the lock we're holding.
+ * Hold a reference to the sock so it doesn't disappear
+ * as we're jumping between locks.
+ */
- if (sk->sk_state &
- (PPPOX_CONNECTED | PPPOX_BOUND)) {
- pppox_unbind_sock(sk);
- dev_put(dev);
- sk->sk_state = PPPOX_ZOMBIE;
- sk->sk_state_change(sk);
- }
+ sock_hold(sk);
- release_sock(sk);
+ write_unlock_bh(&pppoe_hash_lock);
+ lock_sock(sk);
- sock_put(sk);
+ if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+ pppox_unbind_sock(sk);
+ sk->sk_state = PPPOX_ZOMBIE;
+ sk->sk_state_change(sk);
+ }
- read_lock_bh(&pppoe_hash_lock);
+ release_sock(sk);
+ sock_put(sk);
- /* Now restart from the beginning of this
- * hash chain. We always NULL out pppoe_dev
- * so we are guaranteed to make forward
- * progress.
- */
- po = item_hash_table[hash];
- continue;
- }
- po = po->next;
+ /* Restart scan at the beginning of this hash chain.
+ * While the lock was dropped the chain contents may
+ * have changed.
+ */
+ write_lock_bh(&pppoe_hash_lock);
+ po = item_hash_table[hash];
}
}
- read_unlock_bh(&pppoe_hash_lock);
+ write_unlock_bh(&pppoe_hash_lock);
}
static int pppoe_device_event(struct notifier_block *this,
@@ -344,10 +329,10 @@ static struct notifier_block pppoe_notifier = {
static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
{
struct pppox_sock *po = pppox_sk(sk);
- struct pppox_sock *relay_po = NULL;
+ struct pppox_sock *relay_po;
if (sk->sk_state & PPPOX_BOUND) {
- struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw;
+ struct pppoe_hdr *ph = pppoe_hdr(skb);
int len = ntohs(ph->length);
skb_pull_rcsum(skb, sizeof(struct pppoe_hdr));
if (pskb_trim_rcsum(skb, len))
@@ -401,7 +386,7 @@ static int pppoe_rcv(struct sk_buff *skb,
if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
goto out;
- ph = (struct pppoe_hdr *) skb->nh.raw;
+ ph = pppoe_hdr(skb);
po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
if (po != NULL)
@@ -433,7 +418,7 @@ static int pppoe_disc_rcv(struct sk_buff *skb,
if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
goto out;
- ph = (struct pppoe_hdr *) skb->nh.raw;
+ ph = pppoe_hdr(skb);
if (ph->code != PADT_CODE)
goto abort;
@@ -514,36 +499,49 @@ static int pppoe_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct pppox_sock *po;
- int error = 0;
if (!sk)
return 0;
- if (sock_flag(sk, SOCK_DEAD))
+ lock_sock(sk);
+ if (sock_flag(sk, SOCK_DEAD)){
+ release_sock(sk);
return -EBADF;
+ }
pppox_unbind_sock(sk);
/* Signal the death of the socket. */
sk->sk_state = PPPOX_DEAD;
+
+ /* Write lock on hash lock protects the entire "po" struct from
+ * concurrent updates via pppoe_flush_dev. The "po" struct should
+ * be considered part of the hash table contents, thus protected
+ * by the hash table lock */
+ write_lock_bh(&pppoe_hash_lock);
+
po = pppox_sk(sk);
if (po->pppoe_pa.sid) {
- delete_item(po->pppoe_pa.sid, po->pppoe_pa.remote, po->pppoe_ifindex);
+ __delete_item(po->pppoe_pa.sid,
+ po->pppoe_pa.remote, po->pppoe_ifindex);
}
- if (po->pppoe_dev)
+ if (po->pppoe_dev) {
dev_put(po->pppoe_dev);
+ po->pppoe_dev = NULL;
+ }
- po->pppoe_dev = NULL;
+ write_unlock_bh(&pppoe_hash_lock);
sock_orphan(sk);
sock->sk = NULL;
skb_queue_purge(&sk->sk_receive_queue);
+ release_sock(sk);
sock_put(sk);
- return error;
+ return 0;
}
@@ -599,14 +597,18 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
po->pppoe_dev = dev;
po->pppoe_ifindex = dev->ifindex;
- if (!(dev->flags & IFF_UP))
+ write_lock_bh(&pppoe_hash_lock);
+ if (!(dev->flags & IFF_UP)){
+ write_unlock_bh(&pppoe_hash_lock);
goto err_put;
+ }
memcpy(&po->pppoe_pa,
&sp->sa_addr.pppoe,
sizeof(struct pppoe_addr));
- error = set_item(po);
+ error = __set_item(po);
+ write_unlock_bh(&pppoe_hash_lock);
if (error < 0)
goto err_put;
@@ -762,10 +764,10 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd,
static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb;
struct sock *sk = sock->sk;
struct pppox_sock *po = pppox_sk(sk);
- int error = 0;
+ int error;
struct pppoe_hdr hdr;
struct pppoe_hdr *ph;
struct net_device *dev;
@@ -799,7 +801,7 @@ static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock,
/* Reserve space for headers. */
skb_reserve(skb, dev->hard_header_len);
- skb->nh.raw = skb->data;
+ skb_reset_network_header(skb);
skb->dev = dev;
@@ -869,7 +871,8 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
goto abort;
skb_reserve(skb2, dev->hard_header_len + sizeof(struct pppoe_hdr));
- memcpy(skb_put(skb2, skb->len), skb->data, skb->len);
+ skb_copy_from_linear_data(skb, skb_put(skb2, skb->len),
+ skb->len);
} else {
/* Make a clone so as to not disturb the original skb,
* give dev_queue_xmit something it can free.
@@ -884,7 +887,7 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
skb2->protocol = __constant_htons(ETH_P_PPP_SES);
- skb2->nh.raw = skb2->data;
+ skb_reset_network_header(skb2);
skb2->dev = dev;
@@ -929,10 +932,8 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len, int flags)
{
struct sock *sk = sock->sk;
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb;
int error = 0;
- int len;
- struct pppoe_hdr *ph = NULL;
if (sk->sk_state & PPPOX_BOUND) {
error = -EIO;
@@ -942,26 +943,21 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &error);
- if (error < 0) {
+ if (error < 0)
goto end;
- }
m->msg_namelen = 0;
if (skb) {
- error = 0;
- ph = (struct pppoe_hdr *) skb->nh.raw;
- len = ntohs(ph->length);
+ struct pppoe_hdr *ph = pppoe_hdr(skb);
+ const int len = ntohs(ph->length);
error = memcpy_toiovec(m->msg_iov, (unsigned char *) &ph->tag[0], len);
- if (error < 0)
- goto do_skb_free;
- error = len;
+ if (error == 0)
+ error = len;
}
-do_skb_free:
- if (skb)
- kfree_skb(skb);
+ kfree_skb(skb);
end:
return error;
}
@@ -991,7 +987,7 @@ out:
static __inline__ struct pppox_sock *pppoe_get_idx(loff_t pos)
{
- struct pppox_sock *po = NULL;
+ struct pppox_sock *po;
int i = 0;
for (; i < PPPOE_HASH_SIZE; i++) {
diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c
index 9315046b3f55..3f8115db4d54 100644
--- a/drivers/net/pppox.c
+++ b/drivers/net/pppox.c
@@ -58,7 +58,7 @@ void pppox_unbind_sock(struct sock *sk)
{
/* Clear connection to ppp device, if attached. */
- if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE)) {
+ if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
ppp_unregister_channel(&pppox_sk(sk)->chan);
sk->sk_state = PPPOX_DEAD;
}
diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c
index a8246eb2f8d9..7b80fb7a9d9b 100755
--- a/drivers/net/qla3xxx.c
+++ b/drivers/net/qla3xxx.c
@@ -1873,7 +1873,6 @@ static void ql_process_mac_rx_intr(struct ql3_adapter *qdev,
pci_unmap_len(lrg_buf_cb2, maplen),
PCI_DMA_FROMDEVICE);
prefetch(skb->data);
- skb->dev = qdev->ndev;
skb->ip_summed = CHECKSUM_NONE;
skb->protocol = eth_type_trans(skb, qdev->ndev);
@@ -1928,7 +1927,8 @@ static void ql_process_macip_rx_intr(struct ql3_adapter *qdev,
* Copy the ethhdr from first buffer to second. This
* is necessary for 3022 IP completions.
*/
- memcpy(skb_push(skb2, size), skb1->data + VLAN_ID_LEN, size);
+ skb_copy_from_linear_data_offset(skb1, VLAN_ID_LEN,
+ skb_push(skb2, size), size);
} else {
u16 checksum = le16_to_cpu(ib_ip_rsp_ptr->checksum);
if (checksum &
@@ -1946,7 +1946,6 @@ static void ql_process_macip_rx_intr(struct ql3_adapter *qdev,
skb2->ip_summed = CHECKSUM_UNNECESSARY;
}
}
- skb2->dev = qdev->ndev;
skb2->protocol = eth_type_trans(skb2, qdev->ndev);
netif_receive_skb(skb2);
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 6a77b8a92245..45876a854f00 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -2284,7 +2284,7 @@ static inline u32 rtl8169_tso_csum(struct sk_buff *skb, struct net_device *dev)
return LargeSend | ((mss & MSSMask) << MSSShift);
}
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- const struct iphdr *ip = skb->nh.iph;
+ const struct iphdr *ip = ip_hdr(skb);
if (ip->protocol == IPPROTO_TCP)
return IPCS | TCPCS;
@@ -2586,7 +2586,6 @@ rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
pci_action(tp->pci_dev, le64_to_cpu(desc->addr),
tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
- skb->dev = dev;
skb_put(skb, pkt_size);
skb->protocol = eth_type_trans(skb, dev);
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index b7ff484af3e1..df6b73872fdb 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -115,7 +115,6 @@ static int rionet_rx_clean(struct net_device *ndev)
rnet->rx_skb[i]->data = data;
skb_put(rnet->rx_skb[i], RIO_MAX_MSG_SIZE);
- rnet->rx_skb[i]->dev = ndev;
rnet->rx_skb[i]->protocol =
eth_type_trans(rnet->rx_skb[i], ndev);
error = netif_rx(rnet->rx_skb[i]);
diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c
index d81536f90df6..25c73d47daad 100644
--- a/drivers/net/rrunner.c
+++ b/drivers/net/rrunner.c
@@ -1029,7 +1029,6 @@ static void rx_int(struct net_device *dev, u32 rxlimit, u32 index)
goto defer;
}
}
- skb->dev = dev;
skb->protocol = hippi_type_trans(skb, dev);
netif_rx(skb); /* send it up */
@@ -1452,7 +1451,7 @@ static int rr_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
skb_reserve(new_skb, 8);
skb_put(new_skb, len);
- memcpy(new_skb->data, skb->data, len);
+ skb_copy_from_linear_data(skb, new_skb->data, len);
dev_kfree_skb(skb);
skb = new_skb;
}
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
index 46ebf141ee5a..600d3ff347fc 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -2195,7 +2195,7 @@ static int fill_rxd_3buf(struct s2io_nic *nic, struct RxD_t *rxdp, struct \
frag_list->next = NULL;
tmp = (void *)ALIGN((long)frag_list->data, ALIGN_SIZE + 1);
frag_list->data = tmp;
- frag_list->tail = tmp;
+ skb_reset_tail_pointer(frag_list);
/* Buffer-2 receives L4 data payload */
((struct RxD3*)rxdp)->Buffer2_ptr = pci_map_single(nic->pdev,
@@ -2349,7 +2349,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no)
tmp += ALIGN_SIZE;
tmp &= ~ALIGN_SIZE;
skb->data = (void *) (unsigned long)tmp;
- skb->tail = (void *) (unsigned long)tmp;
+ skb_reset_tail_pointer(skb);
if (!(((struct RxD3*)rxdp)->Buffer0_ptr))
((struct RxD3*)rxdp)->Buffer0_ptr =
diff --git a/drivers/net/saa9730.c b/drivers/net/saa9730.c
index 143958f1ef0a..ad94358ece89 100644
--- a/drivers/net/saa9730.c
+++ b/drivers/net/saa9730.c
@@ -688,7 +688,6 @@ static int lan_saa9730_rx(struct net_device *dev)
} else {
lp->stats.rx_bytes += len;
lp->stats.rx_packets++;
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align */
skb_put(skb, len); /* make room */
eth_copy_and_sum(skb,
diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c
index b9fa4fbb1398..1de3eec1a792 100644
--- a/drivers/net/sb1000.c
+++ b/drivers/net/sb1000.c
@@ -834,7 +834,7 @@ printk("cm0: IP identification: %02x%02x fragment offset: %02x%02x\n", buffer[3
goto dropped_frame;
}
skb->dev = dev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = (unsigned short) buffer[NewDatagramHeaderSkip + 16];
insw(ioaddr, skb_put(skb, NewDatagramDataSize),
NewDatagramDataSize / 2);
diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c
index 103c3174ab54..0a3a379b634c 100644
--- a/drivers/net/sb1250-mac.c
+++ b/drivers/net/sb1250-mac.c
@@ -933,9 +933,6 @@ static int sbdma_add_rcvbuffer(sbmacdma_t *d,struct sk_buff *sb)
}
sbdma_align_skb(sb_new, SMP_CACHE_BYTES, ETHER_ALIGN);
-
- /* mark skbuff owned by our device */
- sb_new->dev = d->sbdma_eth->sbm_dev;
}
else {
sb_new = sb;
diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c
index c32c21af3fdd..5b7284c955dc 100644
--- a/drivers/net/sc92031.c
+++ b/drivers/net/sc92031.c
@@ -814,7 +814,6 @@ static void _sc92031_rx_tasklet(struct net_device *dev)
memcpy(skb_put(skb, pkt_size), rx_ring + rx_ring_offset, pkt_size);
}
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
dev->last_rx = jiffies;
netif_rx(skb);
diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c
index 0d6c95c7aedf..4bce7c4f373c 100644
--- a/drivers/net/seeq8005.c
+++ b/drivers/net/seeq8005.c
@@ -550,7 +550,6 @@ static void seeq8005_rx(struct net_device *dev)
lp->stats.rx_dropped++;
break;
}
- skb->dev = dev;
skb_reserve(skb, 2); /* align data on 16 byte */
buf = skb_put(skb,pkt_len);
diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c
index 52ed522a234c..d8c9c5d66d4f 100644
--- a/drivers/net/sgiseeq.c
+++ b/drivers/net/sgiseeq.c
@@ -318,7 +318,6 @@ static inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp
skb = dev_alloc_skb(len + 2);
if (skb) {
- skb->dev = dev;
skb_reserve(skb, 2);
skb_put(skb, len);
@@ -535,7 +534,7 @@ static int sgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev)
* entry and the HPC got to the end of the chain before we
* added this new entry and restarted it.
*/
- memcpy((char *)(long)td->buf_vaddr, skb->data, skblen);
+ skb_copy_from_linear_data(skb, (char *)(long)td->buf_vaddr, skblen);
if (len != skblen)
memset((char *)(long)td->buf_vaddr + skb->len, 0, len-skblen);
td->tdma.cntinfo = (len & HPCDMA_BCNT) |
diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c
index 34463ce6f132..bc8de48da313 100644
--- a/drivers/net/sis190.c
+++ b/drivers/net/sis190.c
@@ -632,7 +632,6 @@ static int sis190_rx_interrupt(struct net_device *dev,
pci_action(tp->pci_dev, le32_to_cpu(desc->addr),
tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
- skb->dev = dev;
skb_put(skb, pkt_size);
skb->protocol = eth_type_trans(skb, dev);
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index b3750f284279..2cb2e156c758 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -1160,7 +1160,6 @@ sis900_init_rx_ring(struct net_device *net_dev)
buffer */
break;
}
- skb->dev = net_dev;
sis_priv->rx_skbuff[i] = skb;
sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE;
sis_priv->rx_ring[i].bufptr = pci_map_single(sis_priv->pci_dev,
@@ -1754,6 +1753,25 @@ static int sis900_rx(struct net_device *net_dev)
sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
} else {
struct sk_buff * skb;
+ struct sk_buff * rx_skb;
+
+ pci_unmap_single(sis_priv->pci_dev,
+ sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+
+ /* refill the Rx buffer, what if there is not enought
+ * memory for new socket buffer ?? */
+ if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
+ /*
+ * Not enough memory to refill the buffer
+ * so we need to recycle the old one so
+ * as to avoid creating a memory hole
+ * in the rx ring
+ */
+ skb = sis_priv->rx_skbuff[entry];
+ sis_priv->stats.rx_dropped++;
+ goto refill_rx_ring;
+ }
/* This situation should never happen, but due to
some unknow bugs, it is possible that
@@ -1768,14 +1786,11 @@ static int sis900_rx(struct net_device *net_dev)
break;
}
- pci_unmap_single(sis_priv->pci_dev,
- sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE,
- PCI_DMA_FROMDEVICE);
/* give the socket buffer to upper layers */
- skb = sis_priv->rx_skbuff[entry];
- skb_put(skb, rx_size);
- skb->protocol = eth_type_trans(skb, net_dev);
- netif_rx(skb);
+ rx_skb = sis_priv->rx_skbuff[entry];
+ skb_put(rx_skb, rx_size);
+ rx_skb->protocol = eth_type_trans(rx_skb, net_dev);
+ netif_rx(rx_skb);
/* some network statistics */
if ((rx_status & BCAST) == MCAST)
@@ -1783,33 +1798,13 @@ static int sis900_rx(struct net_device *net_dev)
net_dev->last_rx = jiffies;
sis_priv->stats.rx_bytes += rx_size;
sis_priv->stats.rx_packets++;
-
- /* refill the Rx buffer, what if there is not enought
- * memory for new socket buffer ?? */
- if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
- /* not enough memory for skbuff, this makes a
- * "hole" on the buffer ring, it is not clear
- * how the hardware will react to this kind
- * of degenerated buffer */
- if (netif_msg_rx_status(sis_priv))
- printk(KERN_INFO "%s: Memory squeeze,"
- "deferring packet.\n",
- net_dev->name);
- sis_priv->rx_skbuff[entry] = NULL;
- /* reset buffer descriptor state */
- sis_priv->rx_ring[entry].cmdsts = 0;
- sis_priv->rx_ring[entry].bufptr = 0;
- sis_priv->stats.rx_dropped++;
- sis_priv->cur_rx++;
- break;
- }
- skb->dev = net_dev;
+ sis_priv->dirty_rx++;
+refill_rx_ring:
sis_priv->rx_skbuff[entry] = skb;
sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
sis_priv->rx_ring[entry].bufptr =
pci_map_single(sis_priv->pci_dev, skb->data,
RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
- sis_priv->dirty_rx++;
}
sis_priv->cur_rx++;
entry = sis_priv->cur_rx % NUM_RX_DESC;
@@ -1836,7 +1831,6 @@ static int sis900_rx(struct net_device *net_dev)
sis_priv->stats.rx_dropped++;
break;
}
- skb->dev = net_dev;
sis_priv->rx_skbuff[entry] = skb;
sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
sis_priv->rx_ring[entry].bufptr =
diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c
index e94ab256b540..e0a93005e6dc 100644
--- a/drivers/net/sk98lin/skge.c
+++ b/drivers/net/sk98lin/skge.c
@@ -1562,10 +1562,10 @@ struct sk_buff *pMessage) /* pointer to send-message */
pTxd->pMBuf = pMessage;
if (pMessage->ip_summed == CHECKSUM_PARTIAL) {
- u16 hdrlen = pMessage->h.raw - pMessage->data;
+ u16 hdrlen = skb_transport_offset(pMessage);
u16 offset = hdrlen + pMessage->csum_offset;
- if ((pMessage->h.ipiph->protocol == IPPROTO_UDP ) &&
+ if ((ipip_hdr(pMessage)->protocol == IPPROTO_UDP) &&
(pAC->GIni.GIChipRev == 0) &&
(pAC->GIni.GIChipId == CHIP_ID_YUKON)) {
pTxd->TBControl = BMU_TCP_CHECK;
@@ -1681,7 +1681,7 @@ struct sk_buff *pMessage) /* pointer to send-message */
** Does the HW need to evaluate checksum for TCP or UDP packets?
*/
if (pMessage->ip_summed == CHECKSUM_PARTIAL) {
- u16 hdrlen = pMessage->h.raw - pMessage->data;
+ u16 hdrlen = skb_transport_offset(pMessage);
u16 offset = hdrlen + pMessage->csum_offset;
Control = BMU_STFWD;
@@ -1691,7 +1691,7 @@ struct sk_buff *pMessage) /* pointer to send-message */
** opcode for udp is not working in the hardware yet
** (Revision 2.0)
*/
- if ((pMessage->h.ipiph->protocol == IPPROTO_UDP ) &&
+ if ((ipip_hdr(pMessage)->protocol == IPPROTO_UDP) &&
(pAC->GIni.GIChipRev == 0) &&
(pAC->GIni.GIChipId == CHIP_ID_YUKON)) {
Control |= BMU_TCP_CHECK;
@@ -2127,7 +2127,7 @@ rx_start:
(dma_addr_t) PhysAddr,
FrameLength,
PCI_DMA_FROMDEVICE);
- memcpy(pNewMsg->data, pMsg, FrameLength);
+ skb_copy_to_linear_data(pNewMsg, pMsg, FrameLength);
pci_dma_sync_single_for_device(pAC->PciDev,
(dma_addr_t) PhysAddr,
@@ -2193,7 +2193,6 @@ rx_start:
SK_PNMI_CNT_RX_OCTETS_DELIVERED(pAC,
FrameLength, pRxPort->PortIndex);
- pMsg->dev = pAC->dev[pRxPort->PortIndex];
pMsg->protocol = eth_type_trans(pMsg,
pAC->dev[pRxPort->PortIndex]);
netif_rx(pMsg);
@@ -2246,7 +2245,6 @@ rx_start:
(IFF_PROMISC | IFF_ALLMULTI)) != 0 ||
(ForRlmt & SK_RLMT_RX_PROTOCOL) ==
SK_RLMT_RX_PROTOCOL) {
- pMsg->dev = pAC->dev[pRxPort->PortIndex];
pMsg->protocol = eth_type_trans(pMsg,
pAC->dev[pRxPort->PortIndex]);
netif_rx(pMsg);
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c
index 9733a11c6146..a7ef6c8b7721 100644
--- a/drivers/net/skfp/skfddi.c
+++ b/drivers/net/skfp/skfddi.c
@@ -1680,7 +1680,6 @@ void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
rxd->rxd_os.skb = NULL;
skb_trim(skb, len);
skb->protocol = fddi_type_trans(skb, bp->dev);
- skb->dev = bp->dev; /* pass up device pointer */
netif_rx(skb);
bp->dev->last_rx = jiffies;
@@ -1938,7 +1937,7 @@ int mac_drv_rx_init(struct s_smc *smc, int len, int fc,
}
skb_reserve(skb, 3);
skb_put(skb, len);
- memcpy(skb->data, look_ahead, len);
+ skb_copy_to_linear_data(skb, look_ahead, len);
// deliver frame to system
skb->protocol = fddi_type_trans(skb, smc->os.dev);
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index d476a3cc2e94..f1a0e6c0fbdd 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -2654,12 +2654,12 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
td->dma_hi = map >> 32;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- int offset = skb->h.raw - skb->data;
+ const int offset = skb_transport_offset(skb);
/* This seems backwards, but it is what the sk98lin
* does. Looks like hardware is wrong?
*/
- if (skb->h.ipiph->protocol == IPPROTO_UDP
+ if (ipip_hdr(skb)->protocol == IPPROTO_UDP
&& hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON)
control = BMU_TCP_CHECK;
else
@@ -2950,7 +2950,7 @@ static struct sk_buff *skge_rx_get(struct net_device *dev,
pci_dma_sync_single_for_cpu(skge->hw->pdev,
pci_unmap_addr(e, mapaddr),
len, PCI_DMA_FROMDEVICE);
- memcpy(skb->data, e->skb->data, len);
+ skb_copy_from_linear_data(e->skb, skb->data, len);
pci_dma_sync_single_for_device(skge->hw->pdev,
pci_unmap_addr(e, mapaddr),
len, PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 4a009b7b1777..238c2ca34da6 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -32,6 +32,7 @@
#include <linux/ethtool.h>
#include <linux/pci.h>
#include <linux/ip.h>
+#include <net/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <linux/delay.h>
@@ -49,7 +50,7 @@
#include "sky2.h"
#define DRV_NAME "sky2"
-#define DRV_VERSION "1.13"
+#define DRV_VERSION "1.14"
#define PFX DRV_NAME " "
/*
@@ -123,7 +124,10 @@ static const struct pci_device_id sky2_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) }, /* 88E8050 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) }, /* 88E8053 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4363) }, /* 88E8055 */
+#ifdef broken
+ /* This device causes data corruption problems that are not resolved */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4364) }, /* 88E8056 */
+#endif
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4366) }, /* 88EC036 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4367) }, /* 88EC032 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4368) }, /* 88EC034 */
@@ -740,12 +744,17 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
if (hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX) {
sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8);
sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8);
- if (hw->dev[port]->mtu > ETH_DATA_LEN) {
- /* set Tx GMAC FIFO Almost Empty Threshold */
- sky2_write32(hw, SK_REG(port, TX_GMF_AE_THR), 0x180);
- /* Disable Store & Forward mode for TX */
- sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_STFW_DIS);
- }
+
+ /* set Tx GMAC FIFO Almost Empty Threshold */
+ sky2_write32(hw, SK_REG(port, TX_GMF_AE_THR),
+ (ECU_JUMBO_WM << 16) | ECU_AE_THR);
+
+ if (hw->dev[port]->mtu > ETH_DATA_LEN)
+ sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
+ TX_JUMBO_ENA | TX_STFW_DIS);
+ else
+ sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
+ TX_JUMBO_DIS | TX_STFW_ENA);
}
}
@@ -1278,7 +1287,7 @@ static int sky2_up(struct net_device *dev)
/* Set almost empty threshold */
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_write16(hw, Q_ADDR(txqaddr[port], Q_AL), ECU_TXFF_LEV);
sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
TX_RING_SIZE - 1);
@@ -1383,8 +1392,8 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
/* Check for TCP Segmentation Offload */
mss = skb_shinfo(skb)->gso_size;
if (mss != 0) {
- mss += ((skb->h.th->doff - 5) * 4); /* TCP options */
- mss += (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);
+ mss += tcp_optlen(skb); /* TCP options */
+ mss += ip_hdrlen(skb) + sizeof(struct tcphdr);
mss += ETH_HLEN;
if (mss != sky2->tx_last_mss) {
@@ -1412,14 +1421,14 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
/* Handle TCP checksum offload */
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- unsigned offset = skb->h.raw - skb->data;
+ const unsigned offset = skb_transport_offset(skb);
u32 tcpsum;
tcpsum = offset << 16; /* sum start */
tcpsum |= offset + skb->csum_offset; /* sum write */
ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
- if (skb->nh.iph->protocol == IPPROTO_UDP)
+ if (ip_hdr(skb)->protocol == IPPROTO_UDP)
ctrl |= UDPTCP;
if (tcpsum != sky2->tx_tcpsum) {
@@ -1584,13 +1593,6 @@ 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 || hw->chip_rev == CHIP_REV_YU_EC_U_B0))
- 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);
@@ -1890,6 +1892,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
{
struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
int err;
u16 ctl, mode;
u32 imask;
@@ -1897,9 +1900,8 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
return -EINVAL;
- /* TSO on Yukon Ultra and MTU > 1500 not supported */
- if (hw->chip_id == CHIP_ID_YUKON_EC_U && new_mtu > ETH_DATA_LEN)
- dev->features &= ~NETIF_F_TSO;
+ if (new_mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_FE)
+ return -EINVAL;
if (!netif_running(dev)) {
dev->mtu = new_mtu;
@@ -1915,8 +1917,18 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
synchronize_irq(hw->pdev->irq);
- ctl = gma_read16(hw, sky2->port, GM_GP_CTRL);
- gma_write16(hw, sky2->port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA);
+ if (hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX) {
+ if (new_mtu > ETH_DATA_LEN) {
+ sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
+ TX_JUMBO_ENA | TX_STFW_DIS);
+ dev->features &= NETIF_F_TSO | NETIF_F_SG | NETIF_F_IP_CSUM;
+ } else
+ sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
+ TX_JUMBO_DIS | TX_STFW_ENA);
+ }
+
+ ctl = gma_read16(hw, port, GM_GP_CTRL);
+ gma_write16(hw, port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA);
sky2_rx_stop(sky2);
sky2_rx_clean(sky2);
@@ -1928,9 +1940,9 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
if (dev->mtu > ETH_DATA_LEN)
mode |= GM_SMOD_JUMBO_ENA;
- gma_write16(hw, sky2->port, GM_SERIAL_MODE, mode);
+ gma_write16(hw, port, GM_SERIAL_MODE, mode);
- sky2_write8(hw, RB_ADDR(rxqaddr[sky2->port], RB_CTRL), RB_ENA_OP_MD);
+ sky2_write8(hw, RB_ADDR(rxqaddr[port], RB_CTRL), RB_ENA_OP_MD);
err = sky2_rx_start(sky2);
sky2_write32(hw, B0_IMSK, imask);
@@ -1938,7 +1950,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
if (err)
dev_close(dev);
else {
- gma_write16(hw, sky2->port, GM_GP_CTRL, ctl);
+ gma_write16(hw, port, GM_GP_CTRL, ctl);
netif_poll_enable(hw->dev[0]);
netif_wake_queue(dev);
@@ -1959,7 +1971,7 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2,
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_copy_from_linear_data(re->skb, 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,
@@ -2340,26 +2352,22 @@ static void sky2_mac_intr(struct sky2_hw *hw, unsigned port)
}
}
-/* This should never happen it is a fatal situation */
-static void sky2_descriptor_error(struct sky2_hw *hw, unsigned port,
- const char *rxtx, u32 mask)
+/* This should never happen it is a bug. */
+static void sky2_le_error(struct sky2_hw *hw, unsigned port,
+ u16 q, unsigned ring_size)
{
struct net_device *dev = hw->dev[port];
struct sky2_port *sky2 = netdev_priv(dev);
- u32 imask;
-
- printk(KERN_ERR PFX "%s: %s descriptor error (hardware problem)\n",
- dev ? dev->name : "<not registered>", rxtx);
+ unsigned idx;
+ const u64 *le = (q == Q_R1 || q == Q_R2)
+ ? (u64 *) sky2->rx_le : (u64 *) sky2->tx_le;
- imask = sky2_read32(hw, B0_IMSK);
- imask &= ~mask;
- sky2_write32(hw, B0_IMSK, imask);
+ idx = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX));
+ printk(KERN_ERR PFX "%s: descriptor error q=%#x get=%u [%llx] put=%u\n",
+ dev->name, (unsigned) q, idx, (unsigned long long) le[idx],
+ (unsigned) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX)));
- if (dev) {
- spin_lock(&sky2->phy_lock);
- sky2_link_down(sky2);
- spin_unlock(&sky2->phy_lock);
- }
+ sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK);
}
/* If idle then force a fake soft NAPI poll once a second
@@ -2383,23 +2391,15 @@ static void sky2_idle(unsigned long arg)
mod_timer(&hw->idle_timer, jiffies + msecs_to_jiffies(idle_timeout));
}
-
-static int sky2_poll(struct net_device *dev0, int *budget)
+/* Hardware/software error handling */
+static void sky2_err_intr(struct sky2_hw *hw, u32 status)
{
- struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw;
- int work_limit = min(dev0->quota, *budget);
- int work_done = 0;
- u32 status = sky2_read32(hw, B0_Y2_SP_EISR);
+ if (net_ratelimit())
+ dev_warn(&hw->pdev->dev, "error interrupt status=%#x\n", status);
if (status & Y2_IS_HW_ERR)
sky2_hw_intr(hw);
- if (status & Y2_IS_IRQ_PHY1)
- sky2_phy_intr(hw, 0);
-
- if (status & Y2_IS_IRQ_PHY2)
- sky2_phy_intr(hw, 1);
-
if (status & Y2_IS_IRQ_MAC1)
sky2_mac_intr(hw, 0);
@@ -2407,16 +2407,33 @@ static int sky2_poll(struct net_device *dev0, int *budget)
sky2_mac_intr(hw, 1);
if (status & Y2_IS_CHK_RX1)
- sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1);
+ sky2_le_error(hw, 0, Q_R1, RX_LE_SIZE);
if (status & Y2_IS_CHK_RX2)
- sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2);
+ sky2_le_error(hw, 1, Q_R2, RX_LE_SIZE);
if (status & Y2_IS_CHK_TXA1)
- sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1);
+ sky2_le_error(hw, 0, Q_XA1, TX_RING_SIZE);
if (status & Y2_IS_CHK_TXA2)
- sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2);
+ sky2_le_error(hw, 1, Q_XA2, TX_RING_SIZE);
+}
+
+static int sky2_poll(struct net_device *dev0, int *budget)
+{
+ struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw;
+ int work_limit = min(dev0->quota, *budget);
+ int work_done = 0;
+ u32 status = sky2_read32(hw, B0_Y2_SP_EISR);
+
+ if (unlikely(status & Y2_IS_ERROR))
+ sky2_err_intr(hw, status);
+
+ if (status & Y2_IS_IRQ_PHY1)
+ sky2_phy_intr(hw, 0);
+
+ if (status & Y2_IS_IRQ_PHY2)
+ sky2_phy_intr(hw, 1);
work_done = sky2_status_intr(hw, work_limit);
if (work_done < work_limit) {
@@ -2534,16 +2551,14 @@ static void sky2_reset(struct sky2_hw *hw)
int i;
/* disable ASF */
- if (hw->chip_id <= CHIP_ID_YUKON_EC) {
- if (hw->chip_id == CHIP_ID_YUKON_EX) {
- status = sky2_read16(hw, HCU_CCSR);
- status &= ~(HCU_CCSR_AHB_RST | HCU_CCSR_CPU_RST_MODE |
- HCU_CCSR_UC_STATE_MSK);
- sky2_write16(hw, HCU_CCSR, status);
- } else
- sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET);
- sky2_write16(hw, B0_CTST, Y2_ASF_DISABLE);
- }
+ if (hw->chip_id == CHIP_ID_YUKON_EX) {
+ status = sky2_read16(hw, HCU_CCSR);
+ status &= ~(HCU_CCSR_AHB_RST | HCU_CCSR_CPU_RST_MODE |
+ HCU_CCSR_UC_STATE_MSK);
+ sky2_write16(hw, HCU_CCSR, status);
+ } else
+ sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET);
+ sky2_write16(hw, B0_CTST, Y2_ASF_DISABLE);
/* do a SW reset */
sky2_write8(hw, B0_CTST, CS_RST_SET);
@@ -3328,6 +3343,36 @@ static void sky2_get_regs(struct net_device *dev, struct ethtool_regs *regs,
regs->len - B3_RI_WTO_R1);
}
+/* In order to do Jumbo packets on these chips, need to turn off the
+ * transmit store/forward. Therefore checksum offload won't work.
+ */
+static int no_tx_offload(struct net_device *dev)
+{
+ const struct sky2_port *sky2 = netdev_priv(dev);
+ const struct sky2_hw *hw = sky2->hw;
+
+ return dev->mtu > ETH_DATA_LEN &&
+ (hw->chip_id == CHIP_ID_YUKON_EX
+ || hw->chip_id == CHIP_ID_YUKON_EC_U);
+}
+
+static int sky2_set_tx_csum(struct net_device *dev, u32 data)
+{
+ if (data && no_tx_offload(dev))
+ return -EINVAL;
+
+ return ethtool_op_set_tx_csum(dev, data);
+}
+
+
+static int sky2_set_tso(struct net_device *dev, u32 data)
+{
+ if (data && no_tx_offload(dev))
+ return -EINVAL;
+
+ return ethtool_op_set_tso(dev, data);
+}
+
static const struct ethtool_ops sky2_ethtool_ops = {
.get_settings = sky2_get_settings,
.set_settings = sky2_set_settings,
@@ -3343,9 +3388,9 @@ static const struct ethtool_ops sky2_ethtool_ops = {
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_tx_csum = ethtool_op_get_tx_csum,
- .set_tx_csum = ethtool_op_set_tx_csum,
+ .set_tx_csum = sky2_set_tx_csum,
.get_tso = ethtool_op_get_tso,
- .set_tso = ethtool_op_set_tso,
+ .set_tso = sky2_set_tso,
.get_rx_csum = sky2_get_rx_csum,
.set_rx_csum = sky2_set_rx_csum,
.get_strings = sky2_get_strings,
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h
index ac24bdc42976..5efb5afc45ba 100644
--- a/drivers/net/sky2.h
+++ b/drivers/net/sky2.h
@@ -288,6 +288,9 @@ enum {
| Y2_IS_CHK_TXA1 | Y2_IS_CHK_RX1,
Y2_IS_PORT_2 = Y2_IS_IRQ_PHY2 | Y2_IS_IRQ_MAC2
| Y2_IS_CHK_TXA2 | Y2_IS_CHK_RX2,
+ Y2_IS_ERROR = Y2_IS_HW_ERR |
+ Y2_IS_IRQ_MAC1 | Y2_IS_CHK_TXA1 | Y2_IS_CHK_RX1 |
+ Y2_IS_IRQ_MAC2 | Y2_IS_CHK_TXA2 | Y2_IS_CHK_RX2,
};
/* B2_IRQM_HWE_MSK 32 bit IRQ Moderation HW Error Mask */
@@ -738,6 +741,11 @@ enum {
TX_GMF_RP = 0x0d70,/* 32 bit Tx GMAC FIFO Read Pointer */
TX_GMF_RSTP = 0x0d74,/* 32 bit Tx GMAC FIFO Restart Pointer */
TX_GMF_RLEV = 0x0d78,/* 32 bit Tx GMAC FIFO Read Level */
+
+ /* Threshold values for Yukon-EC Ultra and Extreme */
+ ECU_AE_THR = 0x0070, /* Almost Empty Threshold */
+ ECU_TXFF_LEV = 0x01a0, /* Tx BMU FIFO Level */
+ ECU_JUMBO_WM = 0x0080, /* Jumbo Mode Watermark */
};
/* Descriptor Poll Timer Registers */
@@ -1631,6 +1639,9 @@ enum {
TX_VLAN_TAG_ON = 1<<25,/* enable VLAN tagging */
TX_VLAN_TAG_OFF = 1<<24,/* disable VLAN tagging */
+ TX_JUMBO_ENA = 1<<23,/* PCI Jumbo Mode enable (Yukon-EC Ultra) */
+ TX_JUMBO_DIS = 1<<22,/* PCI Jumbo Mode enable (Yukon-EC Ultra) */
+
GMF_WSP_TST_ON = 1<<18,/* Write Shadow Pointer Test On */
GMF_WSP_TST_OFF = 1<<17,/* Write Shadow Pointer Test Off */
GMF_WSP_STEP = 1<<16,/* Write Shadow Pointer Step/Increment */
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index 2f4b1de7a2b4..65bd20fac820 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -363,7 +363,7 @@ sl_bump(struct slip *sl)
}
skb->dev = sl->dev;
memcpy(skb_put(skb,count), sl->rbuff, count);
- skb->mac.raw=skb->data;
+ skb_reset_mac_header(skb);
skb->protocol=htons(ETH_P_IP);
netif_rx(skb);
sl->dev->last_rx = jiffies;
diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c
index c95614131980..8a2109a913b6 100644
--- a/drivers/net/smc911x.c
+++ b/drivers/net/smc911x.c
@@ -502,7 +502,6 @@ static inline void smc911x_rcv(struct net_device *dev)
DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name,);
PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64);
dev->last_rx = jiffies;
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
lp->stats.rx_packets++;
@@ -1307,7 +1306,6 @@ smc911x_rx_dma_irq(int dma, void *data)
lp->current_rx_skb = NULL;
PRINT_PKT(skb->data, skb->len);
dev->last_rx = jiffies;
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
lp->stats.rx_packets++;
diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c
index bd6e84506c29..36c1ebadbf20 100644
--- a/drivers/net/smc9194.c
+++ b/drivers/net/smc9194.c
@@ -1262,7 +1262,6 @@ static void smc_rcv(struct net_device *dev)
skb_reserve( skb, 2 ); /* 16 bit alignment */
- skb->dev = dev;
data = skb_put( skb, packet_length);
#ifdef USE_32_BIT
diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c
index 49f4b7712ebf..01cc3c742c38 100644
--- a/drivers/net/smc91x.c
+++ b/drivers/net/smc91x.c
@@ -568,7 +568,6 @@ static inline void smc_rcv(struct net_device *dev)
PRINT_PKT(data, packet_len - 4);
dev->last_rx = jiffies;
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
lp->stats.rx_packets++;
diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c
index ed7aa0a5acca..c6320c719931 100644
--- a/drivers/net/sonic.c
+++ b/drivers/net/sonic.c
@@ -85,7 +85,6 @@ static int sonic_open(struct net_device *dev)
dev->name);
return -ENOMEM;
}
- skb->dev = dev;
/* align IP header unless DMA requires otherwise */
if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
skb_reserve(skb, 2);
@@ -451,7 +450,6 @@ static void sonic_rx(struct net_device *dev)
lp->stats.rx_dropped++;
break;
}
- new_skb->dev = dev;
/* provide 16 byte IP header alignment unless DMA requires otherwise */
if(SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
skb_reserve(new_skb, 2);
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index 3b91af89e4c7..230da14b1b68 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -719,8 +719,8 @@ spider_net_prepare_tx_descr(struct spider_net_card *card,
SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_NOCS;
spin_unlock_irqrestore(&chain->lock, flags);
- if (skb->protocol == htons(ETH_P_IP))
- switch (skb->nh.iph->protocol) {
+ if (skb->protocol == htons(ETH_P_IP) && skb->ip_summed == CHECKSUM_PARTIAL)
+ switch (ip_hdr(skb)->protocol) {
case IPPROTO_TCP:
hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP;
break;
@@ -990,7 +990,6 @@ spider_net_pass_skb_up(struct spider_net_descr *descr,
netdev = card->netdev;
skb = descr->skb;
- skb->dev = netdev;
skb_put(skb, hwdescr->valid_size);
/* the card seems to add 2 bytes of junk in front
diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c
index 8bba2e3da7e1..9d6e454a8f98 100644
--- a/drivers/net/starfire.c
+++ b/drivers/net/starfire.c
@@ -1452,7 +1452,6 @@ static int __netdev_rx(struct net_device *dev, int *quota)
to a minimally-sized skbuff. */
if (pkt_len < rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(np->pci_dev,
np->rx_info[entry].mapping,
diff --git a/drivers/net/sun3_82586.c b/drivers/net/sun3_82586.c
index 4757aa647c7a..396c3d961f88 100644
--- a/drivers/net/sun3_82586.c
+++ b/drivers/net/sun3_82586.c
@@ -775,7 +775,6 @@ static void sun3_82586_rcv_int(struct net_device *dev)
skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
if(skb != NULL)
{
- skb->dev = dev;
skb_reserve(skb,2);
skb_put(skb,totlen);
eth_copy_and_sum(skb,(char *) p->base+swab32((unsigned long) rbd->buffer),totlen,0);
@@ -1027,7 +1026,7 @@ static int sun3_82586_send_packet(struct sk_buff *skb, struct net_device *dev)
memset((char *)p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN);
len = ETH_ZLEN;
}
- memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
+ skb_copy_from_linear_data(skb, p->xmit_cbuffs[p->xmit_count], skb->len);
#if (NUM_XMIT_BUFFS == 1)
# ifdef NO_NOPCOMMANDS
diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c
index 7bee45b42a2c..791e081fdc15 100644
--- a/drivers/net/sun3lance.c
+++ b/drivers/net/sun3lance.c
@@ -629,7 +629,7 @@ static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
head->length = (-len) | 0xf000;
head->misc = 0;
- memcpy( PKTBUF_ADDR(head), (void *)skb->data, skb->len );
+ skb_copy_from_linear_data(skb, PKTBUF_ADDR(head), skb->len);
if (len != skb->len)
memset(PKTBUF_ADDR(head) + skb->len, 0, len-skb->len);
@@ -851,10 +851,9 @@ static int lance_rx( struct net_device *dev )
}
- skb->dev = dev;
skb_reserve( skb, 2 ); /* 16 byte align */
skb_put( skb, pkt_len ); /* Make room */
-// memcpy( skb->data, PKTBUF_ADDR(head), pkt_len );
+// skb_copy_to_linear_data(skb, PKTBUF_ADDR(head), pkt_len);
eth_copy_and_sum(skb,
PKTBUF_ADDR(head),
pkt_len, 0);
diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c
index 18f88853e1e5..2ad8d58dee3b 100644
--- a/drivers/net/sunbmac.c
+++ b/drivers/net/sunbmac.c
@@ -855,7 +855,6 @@ static void bigmac_rx(struct bigmac *bp)
drops++;
goto drop_it;
}
- copy_skb->dev = bp->dev;
skb_reserve(copy_skb, 2);
skb_put(copy_skb, len);
sbus_dma_sync_single_for_cpu(bp->bigmac_sdev,
diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c
index c06ecc8002b9..f51ba31970aa 100644
--- a/drivers/net/sundance.c
+++ b/drivers/net/sundance.c
@@ -1308,7 +1308,6 @@ static void rx_poll(unsigned long data)
to a minimally-sized skbuff. */
if (pkt_len < rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(np->pci_dev,
desc->frag[0].addr,
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index 08ea61db46fe..5da73212ac91 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c
@@ -64,11 +64,9 @@
#include <asm/uaccess.h>
#include <asm/irq.h>
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
#include <asm/idprom.h>
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-#include <asm/pbm.h>
+#include <asm/prom.h>
#endif
#ifdef CONFIG_PPC_PMAC
@@ -845,11 +843,10 @@ static int gem_rx(struct gem *gp, int work_to_do)
goto drop_it;
}
- copy_skb->dev = gp->dev;
skb_reserve(copy_skb, 2);
skb_put(copy_skb, len);
pci_dma_sync_single_for_cpu(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
- memcpy(copy_skb->data, skb->data, len);
+ skb_copy_from_linear_data(skb, copy_skb->data, len);
pci_dma_sync_single_for_device(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
/* We'll reuse the original ring buffer. */
@@ -1029,10 +1026,8 @@ static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev)
ctrl = 0;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- u64 csum_start_off, csum_stuff_off;
-
- csum_start_off = (u64) (skb->h.raw - skb->data);
- csum_stuff_off = csum_start_off + skb->csum_offset;
+ const u64 csum_start_off = skb_transport_offset(skb);
+ const u64 csum_stuff_off = csum_start_off + skb->csum_offset;
ctrl = (TXDCTRL_CENAB |
(csum_start_off << 15) |
@@ -2849,7 +2844,7 @@ static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return rc;
}
-#if (!defined(__sparc__) && !defined(CONFIG_PPC_PMAC))
+#if (!defined(CONFIG_SPARC) && !defined(CONFIG_PPC_PMAC))
/* Fetch MAC address from vital product data of PCI ROM. */
static int find_eth_addr_in_vpd(void __iomem *rom_base, int len, unsigned char *dev_addr)
{
@@ -2904,36 +2899,19 @@ static void get_gem_mac_nonobp(struct pci_dev *pdev, unsigned char *dev_addr)
static int __devinit gem_get_device_address(struct gem *gp)
{
-#if defined(__sparc__) || defined(CONFIG_PPC_PMAC)
+#if defined(CONFIG_SPARC) || defined(CONFIG_PPC_PMAC)
struct net_device *dev = gp->dev;
-#endif
-
-#if defined(__sparc__)
- struct pci_dev *pdev = gp->pdev;
- struct pcidev_cookie *pcp = pdev->sysdata;
- int use_idprom = 1;
-
- if (pcp != NULL) {
- unsigned char *addr;
- int len;
-
- addr = of_get_property(pcp->prom_node, "local-mac-address",
- &len);
- if (addr && len == 6) {
- use_idprom = 0;
- memcpy(dev->dev_addr, addr, 6);
- }
- }
- if (use_idprom)
- memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
-#elif defined(CONFIG_PPC_PMAC)
const unsigned char *addr;
addr = get_property(gp->of_node, "local-mac-address", NULL);
if (addr == NULL) {
+#ifdef CONFIG_SPARC
+ addr = idprom->id_ethaddr;
+#else
printk("\n");
printk(KERN_ERR "%s: can't get mac-address\n", dev->name);
return -1;
+#endif
}
memcpy(dev->dev_addr, addr, 6);
#else
@@ -3091,7 +3069,7 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
/* On Apple, we want a reference to the Open Firmware device-tree
* node. We use it for clock control.
*/
-#ifdef CONFIG_PPC_PMAC
+#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC)
gp->of_node = pci_device_to_OF_node(pdev);
#endif
diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h
index a70067c85cc9..58cf87c5751e 100644
--- a/drivers/net/sungem.h
+++ b/drivers/net/sungem.h
@@ -1025,7 +1025,7 @@ struct gem {
struct pci_dev *pdev;
struct net_device *dev;
-#ifdef CONFIG_PPC_PMAC
+#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC)
struct device_node *of_node;
#endif
};
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index ef671739cfea..51c3fe2108a3 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -55,9 +55,6 @@
#ifdef CONFIG_PCI
#include <linux/pci.h>
-#ifdef CONFIG_SPARC
-#include <asm/pbm.h>
-#endif
#endif
#include "sunhme.h"
@@ -2058,11 +2055,10 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
goto drop_it;
}
- copy_skb->dev = dev;
skb_reserve(copy_skb, 2);
skb_put(copy_skb, len);
hme_dma_sync_for_cpu(hp, dma_addr, len, DMA_FROMDEVICE);
- memcpy(copy_skb->data, skb->data, len);
+ skb_copy_from_linear_data(skb, copy_skb->data, len);
hme_dma_sync_for_device(hp, dma_addr, len, DMA_FROMDEVICE);
/* Reuse original ring buffer. */
@@ -2270,10 +2266,8 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_flags = TXFLAG_OWN;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- u32 csum_start_off, csum_stuff_off;
-
- csum_start_off = (u32) (skb->h.raw - skb->data);
- csum_stuff_off = csum_start_off + skb->csum_offset;
+ const u32 csum_start_off = skb_transport_offset(skb);
+ const u32 csum_stuff_off = csum_start_off + skb->csum_offset;
tx_flags = (TXFLAG_OWN | TXFLAG_CSENABLE |
((csum_start_off << 14) & TXFLAG_CSBUFBEGIN) |
@@ -2704,7 +2698,7 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe
dev->dev_addr[i] = macaddr[i];
macaddr[5]++;
} else {
- unsigned char *addr;
+ const unsigned char *addr;
int len;
addr = of_get_property(dp, "local-mac-address", &len);
@@ -2986,7 +2980,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
{
struct quattro *qp = NULL;
#ifdef CONFIG_SPARC
- struct pcidev_cookie *pcp;
+ struct device_node *dp;
#endif
struct happy_meal *hp;
struct net_device *dev;
@@ -2998,13 +2992,8 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
/* Now make sure pci_dev cookie is there. */
#ifdef CONFIG_SPARC
- pcp = pdev->sysdata;
- if (pcp == NULL) {
- printk(KERN_ERR "happymeal(PCI): Some PCI device info missing\n");
- return -ENODEV;
- }
-
- strcpy(prom_name, pcp->prom_node->name);
+ dp = pci_device_to_OF_node(pdev);
+ strcpy(prom_name, dp->name);
#else
if (is_quattro_p(pdev))
strcpy(prom_name, "SUNW,qfe");
@@ -3081,11 +3070,11 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
macaddr[5]++;
} else {
#ifdef CONFIG_SPARC
- unsigned char *addr;
+ const unsigned char *addr;
int len;
if (qfe_slot != -1 &&
- (addr = of_get_property(pcp->prom_node,
+ (addr = of_get_property(dp,
"local-mac-address", &len)) != NULL
&& len == 6) {
memcpy(dev->dev_addr, addr, 6);
@@ -3105,7 +3094,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
hp->tcvregs = (hpreg_base + 0x7000UL);
#ifdef CONFIG_SPARC
- hp->hm_revision = of_getintprop_default(pcp->prom_node, "hm-rev", 0xff);
+ hp->hm_revision = of_getintprop_default(dp, "hm-rev", 0xff);
if (hp->hm_revision == 0xff) {
unsigned char prev;
@@ -3300,7 +3289,7 @@ static int __devinit hme_sbus_probe(struct of_device *dev, const struct of_devic
{
struct sbus_dev *sdev = to_sbus_device(&dev->dev);
struct device_node *dp = dev->node;
- char *model = of_get_property(dp, "model", NULL);
+ const char *model = of_get_property(dp, "model", NULL);
int is_qfe = (match->data != NULL);
if (!is_qfe && model && !strcmp(model, "SUNW,sbus-qfe"))
@@ -3314,7 +3303,7 @@ static int __devexit hme_sbus_remove(struct of_device *dev)
struct happy_meal *hp = dev_get_drvdata(&dev->dev);
struct net_device *net_dev = hp->dev;
- unregister_netdevice(net_dev);
+ unregister_netdev(net_dev);
/* XXX qfe parent interrupt... */
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c
index 5b00d79b5573..42722530ab24 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/sunlance.c
@@ -547,7 +547,6 @@ static void lance_rx_dvma(struct net_device *dev)
lp->stats.rx_bytes += len;
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align */
skb_put(skb, len); /* make room */
eth_copy_and_sum(skb,
@@ -721,7 +720,6 @@ static void lance_rx_pio(struct net_device *dev)
lp->stats.rx_bytes += len;
- skb->dev = dev;
skb_reserve (skb, 2); /* 16 byte align */
skb_put(skb, len); /* make room */
lance_piocopy_to_skb(skb, &(ib->rx_buf[entry][0]), len);
@@ -1145,7 +1143,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct lance_init_block *ib = lp->init_block_mem;
ib->btx_ring [entry].length = (-len) | 0xf000;
ib->btx_ring [entry].misc = 0;
- memcpy((char *)&ib->tx_buf [entry][0], skb->data, skblen);
+ skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen);
if (len != skblen)
memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen);
ib->btx_ring [entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN);
@@ -1550,7 +1548,7 @@ static int __exit sunlance_sun4_remove(void)
struct lance_private *lp = dev_get_drvdata(&sun4_sdev.ofdev.dev);
struct net_device *net_dev = lp->dev;
- unregister_netdevice(net_dev);
+ unregister_netdev(net_dev);
lance_free_hwresources(lp);
@@ -1590,7 +1588,7 @@ static int __devexit sunlance_sbus_remove(struct of_device *dev)
struct lance_private *lp = dev_get_drvdata(&dev->dev);
struct net_device *net_dev = lp->dev;
- unregister_netdevice(net_dev);
+ unregister_netdev(net_dev);
lance_free_hwresources(lp);
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index 7874eb1ef043..fa70e0b78af7 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -437,7 +437,6 @@ static void qe_rx(struct sunqe *qep)
drops++;
qep->net_stats.rx_dropped++;
} else {
- skb->dev = qep->dev;
skb_reserve(skb, 2);
skb_put(skb, len);
eth_copy_and_sum(skb, (unsigned char *) this_qbuf,
@@ -593,7 +592,7 @@ static int qe_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Avoid a race... */
qep->qe_block->qe_txd[entry].tx_flags = TXD_UPDATE;
- memcpy(txbuf, skb->data, len);
+ skb_copy_from_linear_data(skb, txbuf, len);
qep->qe_block->qe_txd[entry].tx_addr = txbuf_dvma;
qep->qe_block->qe_txd[entry].tx_flags =
@@ -845,6 +844,8 @@ static int __init qec_ether_init(struct sbus_dev *sdev)
if (!dev)
return -ENOMEM;
+ memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+
qe = netdev_priv(dev);
i = of_getintprop_default(sdev->ofdev.node, "channel#", -1);
@@ -960,7 +961,7 @@ static int __devexit qec_sbus_remove(struct of_device *dev)
struct sunqe *qp = dev_get_drvdata(&dev->dev);
struct net_device *net_dev = qp->dev;
- unregister_netdevice(net_dev);
+ unregister_netdev(net_dev);
sbus_iounmap(qp->qcregs, CREG_REG_SIZE);
sbus_iounmap(qp->mregs, MREGS_REG_SIZE);
diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c
index e3a7e3ceab77..d7741e23f8de 100644
--- a/drivers/net/tc35815.c
+++ b/drivers/net/tc35815.c
@@ -1145,7 +1145,6 @@ tc35815_rx(struct net_device *dev)
break;
}
skb_reserve(skb, 2); /* 16 bit alignment */
- skb->dev = dev;
data = skb_put(skb, pkt_len);
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 256969e1300c..9488f49ea569 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -40,16 +40,16 @@
#include <linux/dma-mapping.h>
#include <net/checksum.h>
+#include <net/ip.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
-#ifdef CONFIG_SPARC64
+#ifdef CONFIG_SPARC
#include <asm/idprom.h>
-#include <asm/oplib.h>
-#include <asm/pbm.h>
+#include <asm/prom.h>
#endif
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
@@ -3349,7 +3349,7 @@ static int tg3_rx(struct tg3 *tp, int budget)
skb_reserve(copy_skb, 2);
skb_put(copy_skb, len);
pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
- memcpy(copy_skb->data, skb->data, len);
+ skb_copy_from_linear_data(skb, copy_skb->data, len);
pci_dma_sync_single_for_device(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
/* We'll reuse the original ring buffer. */
@@ -3908,20 +3908,20 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
mss |= (skb_headlen(skb) - ETH_HLEN) << 9;
else {
- tcp_opt_len = ((skb->h.th->doff - 5) * 4);
- ip_tcp_len = (skb->nh.iph->ihl * 4) +
- sizeof(struct tcphdr);
+ struct iphdr *iph = ip_hdr(skb);
+
+ tcp_opt_len = tcp_optlen(skb);
+ ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr);
- skb->nh.iph->check = 0;
- skb->nh.iph->tot_len = htons(mss + ip_tcp_len +
- tcp_opt_len);
+ iph->check = 0;
+ iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
mss |= (ip_tcp_len + tcp_opt_len) << 9;
}
base_flags |= (TXD_FLAG_CPU_PRE_DMA |
TXD_FLAG_CPU_POST_DMA);
- skb->h.th->check = 0;
+ tcp_hdr(skb)->check = 0;
}
else if (skb->ip_summed == CHECKSUM_PARTIAL)
@@ -4055,6 +4055,7 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev)
mss = 0;
if (skb->len > (tp->dev->mtu + ETH_HLEN) &&
(mss = skb_shinfo(skb)->gso_size) != 0) {
+ struct iphdr *iph;
int tcp_opt_len, ip_tcp_len, hdr_len;
if (skb_header_cloned(skb) &&
@@ -4063,8 +4064,8 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev)
goto out_unlock;
}
- tcp_opt_len = ((skb->h.th->doff - 5) * 4);
- ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);
+ tcp_opt_len = tcp_optlen(skb);
+ ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr);
hdr_len = ip_tcp_len + tcp_opt_len;
if (unlikely((ETH_HLEN + hdr_len) > 80) &&
@@ -4074,34 +4075,31 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev)
base_flags |= (TXD_FLAG_CPU_PRE_DMA |
TXD_FLAG_CPU_POST_DMA);
- skb->nh.iph->check = 0;
- skb->nh.iph->tot_len = htons(mss + hdr_len);
+ iph = ip_hdr(skb);
+ iph->check = 0;
+ iph->tot_len = htons(mss + hdr_len);
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
- skb->h.th->check = 0;
+ tcp_hdr(skb)->check = 0;
base_flags &= ~TXD_FLAG_TCPUDP_CSUM;
- }
- else {
- skb->h.th->check =
- ~csum_tcpudp_magic(skb->nh.iph->saddr,
- skb->nh.iph->daddr,
- 0, IPPROTO_TCP, 0);
- }
+ } else
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0,
+ IPPROTO_TCP,
+ 0);
if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) ||
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) {
- if (tcp_opt_len || skb->nh.iph->ihl > 5) {
+ if (tcp_opt_len || iph->ihl > 5) {
int tsflags;
- tsflags = ((skb->nh.iph->ihl - 5) +
- (tcp_opt_len >> 2));
+ tsflags = (iph->ihl - 5) + (tcp_opt_len >> 2);
mss |= (tsflags << 11);
}
} else {
- if (tcp_opt_len || skb->nh.iph->ihl > 5) {
+ if (tcp_opt_len || iph->ihl > 5) {
int tsflags;
- tsflags = ((skb->nh.iph->ihl - 5) +
- (tcp_opt_len >> 2));
+ tsflags = (iph->ihl - 5) + (tcp_opt_len >> 2);
base_flags |= tsflags << 12;
}
}
@@ -10988,24 +10986,20 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
return err;
}
-#ifdef CONFIG_SPARC64
+#ifdef CONFIG_SPARC
static int __devinit tg3_get_macaddr_sparc(struct tg3 *tp)
{
struct net_device *dev = tp->dev;
struct pci_dev *pdev = tp->pdev;
- struct pcidev_cookie *pcp = pdev->sysdata;
-
- if (pcp != NULL) {
- unsigned char *addr;
- int len;
-
- addr = of_get_property(pcp->prom_node, "local-mac-address",
- &len);
- if (addr && len == 6) {
- memcpy(dev->dev_addr, addr, 6);
- memcpy(dev->perm_addr, dev->dev_addr, 6);
- return 0;
- }
+ struct device_node *dp = pci_device_to_OF_node(pdev);
+ const unsigned char *addr;
+ int len;
+
+ addr = of_get_property(dp, "local-mac-address", &len);
+ if (addr && len == 6) {
+ memcpy(dev->dev_addr, addr, 6);
+ memcpy(dev->perm_addr, dev->dev_addr, 6);
+ return 0;
}
return -ENODEV;
}
@@ -11026,7 +11020,7 @@ static int __devinit tg3_get_device_address(struct tg3 *tp)
u32 hi, lo, mac_offset;
int addr_ok = 0;
-#ifdef CONFIG_SPARC64
+#ifdef CONFIG_SPARC
if (!tg3_get_macaddr_sparc(tp))
return 0;
#endif
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
index f85f00251123..106dc1ef0acb 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/tlan.c
@@ -1112,7 +1112,7 @@ static int TLan_StartTx( struct sk_buff *skb, struct net_device *dev )
if ( bbuf ) {
tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
- memcpy( tail_buffer, skb->data, skb->len );
+ skb_copy_from_linear_data(skb, tail_buffer, skb->len);
} else {
tail_list->buffer[0].address = pci_map_single(priv->pciDev, skb->data, skb->len, PCI_DMA_TODEVICE);
TLan_StoreSKB(tail_list, skb);
@@ -1577,7 +1577,6 @@ u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int )
printk(KERN_INFO "TLAN: Couldn't allocate memory for received data.\n");
else {
head_buffer = priv->rxBuffer + (priv->rxHead * TLAN_MAX_FRAME_SIZE);
- skb->dev = dev;
skb_reserve(skb, 2);
t = (void *) skb_put(skb, frameSize);
@@ -1608,7 +1607,6 @@ u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int )
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
- new_skb->dev = dev;
skb_reserve( new_skb, 2 );
t = (void *) skb_put( new_skb, TLAN_MAX_FRAME_SIZE );
head_list->buffer[0].address = pci_map_single(priv->pciDev, new_skb->data, TLAN_MAX_FRAME_SIZE, PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index 7580bdeacadc..e22a3f5333ef 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -933,20 +933,21 @@ static void xl_rx(struct net_device *dev)
return ;
}
- skb->dev = dev ;
-
while (xl_priv->rx_ring_tail != temp_ring_loc) {
copy_len = xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfraglen & 0x7FFF ;
frame_length -= copy_len ;
pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
- memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, copy_len) ;
+ skb_copy_from_linear_data(xl_priv->rx_ring_skb[xl_priv->rx_ring_tail],
+ skb_put(skb, copy_len),
+ copy_len);
pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
adv_rx_ring(dev) ;
}
/* Now we have found the last fragment */
pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
- memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, frame_length) ;
+ skb_copy_from_linear_data(xl_priv->rx_ring_skb[xl_priv->rx_ring_tail],
+ skb_put(skb,copy_len), frame_length);
/* memcpy(skb_put(skb,frame_length), bus_to_virt(xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr), frame_length) ; */
pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
adv_rx_ring(dev) ;
@@ -967,8 +968,6 @@ static void xl_rx(struct net_device *dev)
return ;
}
- skb->dev = dev ;
-
skb2 = xl_priv->rx_ring_skb[xl_priv->rx_ring_tail] ;
pci_unmap_single(xl_priv->pdev, xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr, xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
skb_put(skb2, frame_length) ;
diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c
index 01d55315ee8c..1e8958ee2d0a 100644
--- a/drivers/net/tokenring/ibmtr.c
+++ b/drivers/net/tokenring/ibmtr.c
@@ -1771,7 +1771,6 @@ static void tr_rx(struct net_device *dev)
/*BMS again, if she comes in with few but leaves with many */
skb_reserve(skb, sizeof(struct trh_hdr) - lan_hdr_len);
skb_put(skb, length);
- skb->dev = dev;
data = skb->data;
rbuffer_len = ntohs(readw(rbuf + offsetof(struct rec_buf, buf_len)));
rbufdata = rbuf + offsetof(struct rec_buf, data);
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index e999feb8c0bb..5d849c089a3b 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -944,8 +944,6 @@ static void streamer_rx(struct net_device *dev)
printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n", dev->name);
streamer_priv->streamer_stats.rx_dropped++;
} else { /* we allocated an skb OK */
- skb->dev = dev;
-
if (buffer_cnt == 1) {
/* release the DMA mapping */
pci_unmap_single(streamer_priv->pci_dev,
@@ -1607,10 +1605,11 @@ static void streamer_arb_cmd(struct net_device *dev)
frame_data, buffer_len);
} while (next_ptr && (buff_off = next_ptr));
+ mac_frame->protocol = tr_type_trans(mac_frame, dev);
#if STREAMER_NETWORK_MONITOR
printk(KERN_WARNING "%s: Received MAC Frame, details: \n",
dev->name);
- mac_hdr = (struct trh_hdr *) mac_frame->data;
+ mac_hdr = tr_hdr(mac_frame);
printk(KERN_WARNING
"%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1],
@@ -1622,8 +1621,6 @@ static void streamer_arb_cmd(struct net_device *dev)
mac_hdr->saddr[2], mac_hdr->saddr[3],
mac_hdr->saddr[4], mac_hdr->saddr[5]);
#endif
- mac_frame->dev = dev;
- mac_frame->protocol = tr_type_trans(mac_frame, dev);
netif_rx(mac_frame);
/* Now tell the card we have dealt with the received frame */
diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
index 8f4ecc1109cb..09b3cfb8e809 100644
--- a/drivers/net/tokenring/olympic.c
+++ b/drivers/net/tokenring/olympic.c
@@ -814,8 +814,6 @@ static void olympic_rx(struct net_device *dev)
olympic_priv->rx_ring_last_received += i ;
olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1) ;
} else {
- skb->dev = dev ;
-
/* Optimise based upon number of buffers used.
If only one buffer is used we can simply swap the buffers around.
If more than one then we must use the new buffer and copy the information
@@ -847,7 +845,9 @@ static void olympic_rx(struct net_device *dev)
pci_dma_sync_single_for_cpu(olympic_priv->pdev,
le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer),
olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
- memcpy(skb_put(skb,length-4),olympic_priv->rx_ring_skb[rx_ring_last_received]->data,length-4) ;
+ skb_copy_from_linear_data(olympic_priv->rx_ring_skb[rx_ring_last_received],
+ skb_put(skb,length - 4),
+ length - 4);
pci_dma_sync_single_for_device(olympic_priv->pdev,
le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer),
olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
@@ -864,7 +864,9 @@ static void olympic_rx(struct net_device *dev)
olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
rx_desc = &(olympic_priv->olympic_rx_ring[rx_ring_last_received]);
cpy_length = (i == 1 ? frag_len : le32_to_cpu(rx_desc->res_length));
- memcpy(skb_put(skb, cpy_length), olympic_priv->rx_ring_skb[rx_ring_last_received]->data, cpy_length) ;
+ skb_copy_from_linear_data(olympic_priv->rx_ring_skb[rx_ring_last_received],
+ skb_put(skb, cpy_length),
+ cpy_length);
pci_dma_sync_single_for_device(olympic_priv->pdev,
le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer),
olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ;
@@ -1440,16 +1442,16 @@ static void olympic_arb_cmd(struct net_device *dev)
next_ptr=readw(buf_ptr+offsetof(struct mac_receive_buffer,next));
} while (next_ptr && (buf_ptr=olympic_priv->olympic_lap + ntohs(next_ptr)));
+ mac_frame->protocol = tr_type_trans(mac_frame, dev);
+
if (olympic_priv->olympic_network_monitor) {
struct trh_hdr *mac_hdr ;
printk(KERN_WARNING "%s: Received MAC Frame, details: \n",dev->name) ;
- mac_hdr = (struct trh_hdr *)mac_frame->data ;
+ mac_hdr = tr_hdr(mac_frame);
printk(KERN_WARNING "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name , mac_hdr->daddr[0], mac_hdr->daddr[1], mac_hdr->daddr[2], mac_hdr->daddr[3], mac_hdr->daddr[4], mac_hdr->daddr[5]) ;
printk(KERN_WARNING "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name , mac_hdr->saddr[0], mac_hdr->saddr[1], mac_hdr->saddr[2], mac_hdr->saddr[3], mac_hdr->saddr[4], mac_hdr->saddr[5]) ;
}
- mac_frame->dev = dev ;
- mac_frame->protocol = tr_type_trans(mac_frame,dev);
- netif_rx(mac_frame) ;
+ netif_rx(mac_frame);
dev->last_rx = jiffies;
drop_frame:
diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c
index cec282a6f62d..9bbea5c8acf4 100644
--- a/drivers/net/tokenring/smctr.c
+++ b/drivers/net/tokenring/smctr.c
@@ -3889,14 +3889,13 @@ static int smctr_process_rx_packet(MAC_HEADER *rmf, __u16 size,
/* Slide data into a sleek skb. */
skb_put(skb, skb->len);
- memcpy(skb->data, rmf, skb->len);
+ skb_copy_to_linear_data(skb, rmf, skb->len);
/* Update Counters */
tp->MacStat.rx_packets++;
tp->MacStat.rx_bytes += skb->len;
/* Kick the packet on up. */
- skb->dev = dev;
skb->protocol = tr_type_trans(skb, dev);
netif_rx(skb);
dev->last_rx = jiffies;
@@ -4476,14 +4475,13 @@ static int smctr_rx_frame(struct net_device *dev)
if (skb) {
skb_put(skb, rx_size);
- memcpy(skb->data, pbuff, rx_size);
+ skb_copy_to_linear_data(skb, pbuff, rx_size);
/* Update Counters */
tp->MacStat.rx_packets++;
tp->MacStat.rx_bytes += skb->len;
/* Kick the packet on up. */
- skb->dev = dev;
skb->protocol = tr_type_trans(skb, dev);
netif_rx(skb);
dev->last_rx = jiffies;
diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c
index ea797ca2b988..12bd294045a7 100644
--- a/drivers/net/tokenring/tms380tr.c
+++ b/drivers/net/tokenring/tms380tr.c
@@ -644,7 +644,7 @@ static int tms380tr_hardware_send_packet(struct sk_buff *skb, struct net_device
dmabuf = 0;
i = tp->TplFree->TPLIndex;
buf = tp->LocalTxBuffers[i];
- memcpy(buf, skb->data, length);
+ skb_copy_from_linear_data(skb, buf, length);
newbuf = ((char *)buf - (char *)tp) + tp->dmabuffer;
}
else {
@@ -2168,7 +2168,6 @@ static void tms380tr_rcv_status_irq(struct net_device *dev)
}
else
{
- skb->dev = dev;
skb_put(skb, tp->MaxPacketSize);
rpl->SkbStat = SKB_DATA_COPY;
ReceiveDataPtr = rpl->MData;
@@ -2179,7 +2178,8 @@ static void tms380tr_rcv_status_irq(struct net_device *dev)
|| rpl->SkbStat == SKB_DMA_DIRECT))
{
if(rpl->SkbStat == SKB_DATA_COPY)
- memcpy(skb->data, ReceiveDataPtr, Length);
+ skb_copy_to_linear_data(skb, ReceiveDataPtr,
+ Length);
/* Deliver frame to system */
rpl->Skb = NULL;
diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
index d92c5c597e16..0bfc2c9c1c08 100644
--- a/drivers/net/tsi108_eth.c
+++ b/drivers/net/tsi108_eth.c
@@ -788,7 +788,6 @@ static int tsi108_complete_rx(struct net_device *dev, int budget)
printk(".\n");
}
- skb->dev = dev;
skb_put(skb, data->rxring[rx].len);
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c
index c82befa209a2..861729806dc1 100644
--- a/drivers/net/tulip/de2104x.c
+++ b/drivers/net/tulip/de2104x.c
@@ -63,7 +63,7 @@ MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number");
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
- || defined(__sparc__) || defined(__ia64__) \
+ || defined(CONFIG_SPARC) || defined(__ia64__) \
|| defined(__sh__) || defined(__mips__)
static int rx_copybreak = 1518;
#else
@@ -435,7 +435,6 @@ static void de_rx (struct de_private *de)
rx_work = 100;
goto rx_next;
}
- copy_skb->dev = de->dev;
if (!copying_skb) {
pci_unmap_single(de->pdev, mapping,
@@ -450,8 +449,8 @@ static void de_rx (struct de_private *de)
} else {
pci_dma_sync_single_for_cpu(de->pdev, mapping, len, PCI_DMA_FROMDEVICE);
skb_reserve(copy_skb, RX_OFFSET);
- memcpy(skb_put(copy_skb, len), skb->data, len);
-
+ skb_copy_from_linear_data(skb, skb_put(copy_skb, len),
+ len);
pci_dma_sync_single_for_device(de->pdev, mapping, len, PCI_DMA_FROMDEVICE);
/* We'll reuse the original ring buffer. */
diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c
index 4b3cd3d8b62a..62143f92c231 100644
--- a/drivers/net/tulip/de4x5.c
+++ b/drivers/net/tulip/de4x5.c
@@ -1160,7 +1160,7 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev)
sprintf(lp->adapter_name,"%s (%s)", name, gendev->bus_id);
lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc);
-#if defined(__alpha__) || defined(__powerpc__) || defined(__sparc_v9__) || defined(DE4X5_DO_MEMCPY)
+#if defined(__alpha__) || defined(__powerpc__) || defined(CONFIG_SPARC) || defined(DE4X5_DO_MEMCPY)
lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + DE4X5_ALIGN;
#endif
lp->rx_ring = dma_alloc_coherent(gendev, lp->dma_size,
@@ -1175,7 +1175,7 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev)
** Set up the RX descriptor ring (Intels)
** Allocate contiguous receive buffers, long word aligned (Alphas)
*/
-#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY)
for (i=0; i<NUM_RX_DESC; i++) {
lp->rx_ring[i].status = 0;
lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
@@ -1252,11 +1252,7 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev)
mii_get_phy(dev);
}
-#ifndef __sparc_v9__
printk(" and requires IRQ%d (provided by %s).\n", dev->irq,
-#else
- printk(" and requires IRQ%x (provided by %s).\n", dev->irq,
-#endif
((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
}
@@ -3627,14 +3623,13 @@ de4x5_alloc_rx_buff(struct net_device *dev, int index, int len)
struct de4x5_private *lp = netdev_priv(dev);
struct sk_buff *p;
-#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY)
struct sk_buff *ret;
u_long i=0, tmp;
p = dev_alloc_skb(IEEE802_3_SZ + DE4X5_ALIGN + 2);
if (!p) return NULL;
- p->dev = dev;
tmp = virt_to_bus(p->data);
i = ((tmp + DE4X5_ALIGN) & ~DE4X5_ALIGN) - tmp;
skb_reserve(p, i);
@@ -3655,7 +3650,6 @@ de4x5_alloc_rx_buff(struct net_device *dev, int index, int len)
p = dev_alloc_skb(len + 2);
if (!p) return NULL;
- p->dev = dev;
skb_reserve(p, 2); /* Align */
if (index < lp->rx_old) { /* Wrapped buffer */
short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c
index 9aeac76184f3..b3a64ca98634 100644
--- a/drivers/net/tulip/dmfe.c
+++ b/drivers/net/tulip/dmfe.c
@@ -682,7 +682,7 @@ static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev)
/* transmit this packet */
txptr = db->tx_insert_ptr;
- memcpy(txptr->tx_buf_ptr, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len);
txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
/* Point to next transmit free descriptor */
@@ -988,14 +988,14 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db)
skb = newskb;
/* size less than COPY_SIZE, allocate a rxlen SKB */
- skb->dev = dev;
skb_reserve(skb, 2); /* 16byte align */
- memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->data, rxlen);
+ skb_copy_from_linear_data(rxptr->rx_skb_ptr,
+ skb_put(skb, rxlen),
+ rxlen);
dmfe_reuse_skb(db, rxptr->rx_skb_ptr);
- } else {
- skb->dev = dev;
+ } else
skb_put(skb, rxlen);
- }
+
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->last_rx = jiffies;
diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c
index e3488d7b8ede..e86df07769a1 100644
--- a/drivers/net/tulip/interrupt.c
+++ b/drivers/net/tulip/interrupt.c
@@ -192,7 +192,6 @@ int tulip_poll(struct net_device *dev, int *budget)
to a minimally-sized skbuff. */
if (pkt_len < tulip_rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(tp->pdev,
tp->rx_buffers[entry].mapping,
@@ -416,7 +415,6 @@ static int tulip_rx(struct net_device *dev)
to a minimally-sized skbuff. */
if (pkt_len < tulip_rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(tp->pdev,
tp->rx_buffers[entry].mapping,
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
index e3774a522372..e9bf526ec534 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/tulip/tulip_core.c
@@ -36,8 +36,8 @@
#include <asm/unaligned.h>
#include <asm/uaccess.h>
-#ifdef __sparc__
-#include <asm/pbm.h>
+#ifdef CONFIG_SPARC
+#include <asm/prom.h>
#endif
static char version[] __devinitdata =
@@ -67,7 +67,7 @@ const char * const medianame[32] = {
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
- || defined(__sparc__) || defined(__ia64__) \
+ || defined(CONFIG_SPARC) || defined(__ia64__) \
|| defined(__sh__) || defined(__mips__)
static int rx_copybreak = 1518;
#else
@@ -91,7 +91,7 @@ static int rx_copybreak = 100;
static int csr0 = 0x01A00000 | 0xE000;
#elif defined(__i386__) || defined(__powerpc__) || defined(__x86_64__)
static int csr0 = 0x01A00000 | 0x8000;
-#elif defined(__sparc__) || defined(__hppa__)
+#elif defined(CONFIG_SPARC) || defined(__hppa__)
/* The UltraSparc PCI controllers will disconnect at every 64-byte
* crossing anyways so it makes no sense to tell Tulip to burst
* any more than that.
@@ -1315,7 +1315,7 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
/* DM9102A has troubles with MRM & clear reserved bits 24:22, 20, 16, 7:1 */
if (tulip_uli_dm_quirk(pdev)) {
csr0 &= ~0x01f100ff;
-#if defined(__sparc__)
+#if defined(CONFIG_SPARC)
csr0 = (csr0 & ~0xff00) | 0xe000;
#endif
}
@@ -1535,23 +1535,19 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
Many PCI BIOSes also incorrectly report the IRQ line, so we correct
that here as well. */
if (sum == 0 || sum == 6*0xff) {
-#if defined(__sparc__)
- struct pcidev_cookie *pcp = pdev->sysdata;
+#if defined(CONFIG_SPARC)
+ struct device_node *dp = pci_device_to_OF_node(pdev);
+ const unsigned char *addr;
+ int len;
#endif
eeprom_missing = 1;
for (i = 0; i < 5; i++)
dev->dev_addr[i] = last_phys_addr[i];
dev->dev_addr[i] = last_phys_addr[i] + 1;
-#if defined(__sparc__)
- if (pcp) {
- unsigned char *addr;
- int len;
-
- addr = of_get_property(pcp->prom_node,
- "local-mac-address", &len);
- if (addr && len == 6)
- memcpy(dev->dev_addr, addr, 6);
- }
+#if defined(CONFIG_SPARC)
+ addr = of_get_property(dp, "local-mac-address", &len);
+ if (addr && len == 6)
+ memcpy(dev->dev_addr, addr, 6);
#endif
#if defined(__i386__) || defined(__x86_64__) /* Patch up x86 BIOS bug. */
if (last_irq)
diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c
index 229158e8e4be..ca2548eb7d63 100644
--- a/drivers/net/tulip/uli526x.c
+++ b/drivers/net/tulip/uli526x.c
@@ -583,7 +583,7 @@ static int uli526x_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* transmit this packet */
txptr = db->tx_insert_ptr;
- memcpy(txptr->tx_buf_ptr, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len);
txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
/* Point to next transmit free descriptor */
@@ -828,14 +828,14 @@ static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info
( (skb = dev_alloc_skb(rxlen + 2) )
!= NULL) ) {
/* size less than COPY_SIZE, allocate a rxlen SKB */
- skb->dev = dev;
skb_reserve(skb, 2); /* 16byte align */
- memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen);
+ memcpy(skb_put(skb, rxlen),
+ skb_tail_pointer(rxptr->rx_skb_ptr),
+ rxlen);
uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
- } else {
- skb->dev = dev;
+ } else
skb_put(skb, rxlen);
- }
+
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->last_rx = jiffies;
@@ -1177,7 +1177,10 @@ static void uli526x_reuse_skb(struct uli526x_board_info *db, struct sk_buff * sk
if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) {
rxptr->rx_skb_ptr = skb;
- rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) );
+ rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev,
+ skb_tail_pointer(skb),
+ RX_ALLOC_SIZE,
+ PCI_DMA_FROMDEVICE));
wmb();
rxptr->rdes0 = cpu_to_le32(0x80000000);
db->rx_avail_cnt++;
@@ -1341,7 +1344,10 @@ static void allocate_rx_buffer(struct uli526x_board_info *db)
if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL )
break;
rxptr->rx_skb_ptr = skb; /* FIXME (?) */
- rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) );
+ rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev,
+ skb_tail_pointer(skb),
+ RX_ALLOC_SIZE,
+ PCI_DMA_FROMDEVICE));
wmb();
rxptr->rdes0 = cpu_to_le32(0x80000000);
rxptr = rxptr->next_rx_desc;
diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c
index 002a05e0722f..5b71ac78bca2 100644
--- a/drivers/net/tulip/winbond-840.c
+++ b/drivers/net/tulip/winbond-840.c
@@ -813,7 +813,6 @@ static void init_rxtx_rings(struct net_device *dev)
np->rx_skbuff[i] = skb;
if (skb == NULL)
break;
- skb->dev = dev; /* Mark as being used by this device. */
np->rx_addr[i] = pci_map_single(np->pci_dev,skb->data,
np->rx_buf_sz,PCI_DMA_FROMDEVICE);
@@ -903,7 +902,7 @@ static void init_registers(struct net_device *dev)
}
#elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__)
i |= 0xE000;
-#elif defined(__sparc__) || defined (CONFIG_PARISC)
+#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC)
i |= 0x4800;
#else
#warning Processor architecture undefined
@@ -1229,7 +1228,6 @@ static int netdev_rx(struct net_device *dev)
to a minimally-sized skbuff. */
if (pkt_len < rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(np->pci_dev,np->rx_addr[entry],
np->rx_skbuff[entry]->len,
@@ -1278,7 +1276,6 @@ static int netdev_rx(struct net_device *dev)
np->rx_skbuff[entry] = skb;
if (skb == NULL)
break; /* Better luck next round. */
- skb->dev = dev; /* Mark as being used by this device. */
np->rx_addr[entry] = pci_map_single(np->pci_dev,
skb->data,
np->rx_buf_sz, PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c
index 61d313049dd0..985a1810ca59 100644
--- a/drivers/net/tulip/xircom_cb.c
+++ b/drivers/net/tulip/xircom_cb.c
@@ -411,9 +411,9 @@ static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev)
sometimes sends more than you ask it to. */
memset(&card->tx_buffer[bufferoffsets[desc]/4],0,1536);
- memcpy(&(card->tx_buffer[bufferoffsets[desc]/4]),skb->data,skb->len);
-
-
+ skb_copy_from_linear_data(skb,
+ &(card->tx_buffer[bufferoffsets[desc] / 4]),
+ skb->len);
/* FIXME: The specification tells us that the length we send HAS to be a multiple of
4 bytes. */
@@ -1207,7 +1207,6 @@ static void investigate_read_descriptor(struct net_device *dev,struct xircom_pri
card->stats.rx_dropped++;
goto out;
}
- skb->dev = dev;
skb_reserve(skb, 2);
eth_copy_and_sum(skb, (unsigned char*)&card->rx_buffer[bufferoffset / 4], pkt_len, 0);
skb_put(skb, pkt_len);
diff --git a/drivers/net/tulip/xircom_tulip_cb.c b/drivers/net/tulip/xircom_tulip_cb.c
index a998c5d0ae9c..f64172927377 100644
--- a/drivers/net/tulip/xircom_tulip_cb.c
+++ b/drivers/net/tulip/xircom_tulip_cb.c
@@ -65,7 +65,7 @@ static int rx_copybreak = 100;
static int csr0 = 0x01A00000 | 0xE000;
#elif defined(__powerpc__)
static int csr0 = 0x01B00000 | 0x8000;
-#elif defined(__sparc__)
+#elif defined(CONFIG_SPARC)
static int csr0 = 0x01B00080 | 0x8000;
#elif defined(__i386__)
static int csr0 = 0x01A00000 | 0x8000;
@@ -915,7 +915,9 @@ xircom_start_xmit(struct sk_buff *skb, struct net_device *dev)
tp->tx_skbuff[entry] = skb;
if (tp->chip_id == X3201_3) {
- memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
+ skb_copy_from_linear_data(skb,
+ tp->tx_aligned_skbuff[entry]->data,
+ skb->len);
tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
} else
tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
@@ -1238,7 +1240,6 @@ xircom_rx(struct net_device *dev)
to a minimally-sized skbuff. */
if (pkt_len < rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
#if ! defined(__alpha__)
eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 5643d1e84ed6..a2c6caaaae93 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -18,6 +18,10 @@
/*
* Changes:
*
+ * Brian Braunstein <linuxkernel@bristyle.com> 2007/03/23
+ * Fixed hw address handling. Now net_device.dev_addr is kept consistent
+ * with tun.dev_addr when the address is set by this module.
+ *
* Mike Kershaw <dragorn@kismetwireless.net> 2005/08/14
* Add TUNSETLINK ioctl to set the link encapsulation
*
@@ -196,7 +200,10 @@ static void tun_net_init(struct net_device *dev)
dev->set_multicast_list = tun_net_mclist;
ether_setup(dev);
- random_ether_addr(dev->dev_addr);
+
+ /* random address already created for us by tun_set_iff, use it */
+ memcpy(dev->dev_addr, tun->dev_addr, min(sizeof(tun->dev_addr), sizeof(dev->dev_addr)) );
+
dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */
break;
}
@@ -254,11 +261,11 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
return -EFAULT;
}
- skb->dev = tun->dev;
switch (tun->flags & TUN_TYPE_MASK) {
case TUN_TUN_DEV:
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->protocol = pi.proto;
+ skb->dev = tun->dev;
break;
case TUN_TAP_DEV:
skb->protocol = eth_type_trans(skb, tun->dev);
@@ -386,8 +393,8 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
* - we are multicast promiscous.
* - we belong to the multicast group.
*/
- memcpy(addr, skb->data,
- min_t(size_t, sizeof addr, skb->len));
+ skb_copy_from_linear_data(skb, addr, min_t(size_t, sizeof addr,
+ skb->len));
bit_nr = ether_crc(sizeof addr, addr) >> 26;
if ((tun->if_flags & IFF_PROMISC) ||
memcmp(addr, tun->dev_addr, sizeof addr) == 0 ||
@@ -636,6 +643,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
return 0;
case SIOCGIFHWADDR:
+ /* Note: the actual net device's address may be different */
memcpy(ifr.ifr_hwaddr.sa_data, tun->dev_addr,
min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
if (copy_to_user( argp, &ifr, sizeof ifr))
@@ -643,16 +651,24 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
return 0;
case SIOCSIFHWADDR:
- /** Set the character device's hardware address. This is used when
- * filtering packets being sent from the network device to the character
- * device. */
- memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data,
- min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
- DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n",
- tun->dev->name,
- tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2],
- tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]);
- return 0;
+ {
+ /* try to set the actual net device's hw address */
+ int ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
+
+ if (ret == 0) {
+ /** Set the character device's hardware address. This is used when
+ * filtering packets being sent from the network device to the character
+ * device. */
+ memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data,
+ min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr));
+ DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n",
+ tun->dev->name,
+ tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2],
+ tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]);
+ }
+
+ return ret;
+ }
case SIOCADDMULTI:
/** Add the specified group to the character device's multicast filter
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
index 0d91d094edd9..f2dd7763cd0b 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -1708,7 +1708,6 @@ typhoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile u32 * ready,
if(pkt_len < rx_copybreak &&
(new_skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- new_skb->dev = tp->dev;
skb_reserve(new_skb, 2);
pci_dma_sync_single_for_cpu(tp->pdev, dma_addr,
PKT_BUF_SZ,
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index f3a972e74e9a..adea290a9d5e 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -1486,7 +1486,6 @@ static int rhine_rx(struct net_device *dev, int limit)
copying to a minimally-sized skbuff. */
if (pkt_len < rx_copybreak &&
(skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(rp->pdev,
rp->rx_skbuff_dma[entry],
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index 8e5d82051bd4..25b75b615188 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -1339,7 +1339,8 @@ static inline int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size,
if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN)
skb_reserve(new_skb, 2);
- memcpy(new_skb->data, rx_skb[0]->data, pkt_size);
+ skb_copy_from_linear_data(rx_skb[0], new_skb->data,
+ pkt_size);
*rx_skb = new_skb;
ret = 0;
}
@@ -1398,7 +1399,6 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
vptr->stats.multicast++;
skb = rd_info->skb;
- skb->dev = vptr->dev;
pci_dma_sync_single_for_cpu(vptr->pdev, rd_info->skb_dma,
vptr->rx_buf_sz, PCI_DMA_FROMDEVICE);
@@ -1428,7 +1428,7 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
PCI_DMA_FROMDEVICE);
skb_put(skb, pkt_len - 4);
- skb->protocol = eth_type_trans(skb, skb->dev);
+ skb->protocol = eth_type_trans(skb, vptr->dev);
stats->rx_bytes += pkt_len;
netif_rx(skb);
@@ -1928,7 +1928,7 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev)
if (pktlen < ETH_ZLEN) {
/* Cannot occur until ZC support */
pktlen = ETH_ZLEN;
- memcpy(tdinfo->buf, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, tdinfo->buf, skb->len);
memset(tdinfo->buf + skb->len, 0, ETH_ZLEN - skb->len);
tdinfo->skb = skb;
tdinfo->skb_dma[0] = tdinfo->buf_dma;
@@ -1944,7 +1944,7 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev)
int nfrags = skb_shinfo(skb)->nr_frags;
tdinfo->skb = skb;
if (nfrags > 6) {
- memcpy(tdinfo->buf, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, tdinfo->buf, skb->len);
tdinfo->skb_dma[0] = tdinfo->buf_dma;
td_ptr->tdesc0.pktsize =
td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]);
@@ -2007,7 +2007,7 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev)
*/
if ((vptr->flags & VELOCITY_FLAGS_TX_CSUM)
&& (skb->ip_summed == CHECKSUM_PARTIAL)) {
- struct iphdr *ip = skb->nh.iph;
+ const struct iphdr *ip = ip_hdr(skb);
if (ip->protocol == IPPROTO_TCP)
td_ptr->tdesc1.TCR |= TCR0_TCPCK;
else if (ip->protocol == IPPROTO_UDP)
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 5b82e4fd0d73..23464735fa88 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -773,7 +773,7 @@ static int sppp_rx_done(struct channel_data *chan)
}
chan->rx_skb->protocol = htons(ETH_P_WAN_PPP);
chan->rx_skb->dev = chan->pppdev.dev;
- chan->rx_skb->mac.raw = chan->rx_skb->data;
+ skb_reset_mac_header(chan->rx_skb);
chan->stats.rx_packets++;
chan->stats.rx_bytes += chan->cosa->rxsize;
netif_rx(chan->rx_skb);
diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c
index a631d1c2fa14..016b3ff3ea5e 100644
--- a/drivers/net/wan/cycx_x25.c
+++ b/drivers/net/wan/cycx_x25.c
@@ -834,7 +834,7 @@ static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd)
++chan->ifstats.rx_packets;
chan->ifstats.rx_bytes += pktlen;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
netif_rx(skb);
dev->last_rx = jiffies; /* timestamp */
}
diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c
index 736987559432..66be20c292b6 100644
--- a/drivers/net/wan/dlci.c
+++ b/drivers/net/wan/dlci.c
@@ -176,7 +176,7 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
if (process)
{
/* we've set up the protocol, so discard the header */
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, header);
dlp->stats.rx_bytes += skb->len;
netif_rx(skb);
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index 25021a7992a9..dca024471455 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -1904,7 +1904,8 @@ static struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv)
struct TxFD *tx_fd = dpriv->tx_fd + last;
skb->len = DUMMY_SKB_SIZE;
- memcpy(skb->data, version, strlen(version)%DUMMY_SKB_SIZE);
+ skb_copy_to_linear_data(skb, version,
+ strlen(version) % DUMMY_SKB_SIZE);
tx_fd->state = FrameEnd | TO_STATE_TX(DUMMY_SKB_SIZE);
tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data,
DUMMY_SKB_SIZE, PCI_DMA_TODEVICE);
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index c45d6a83339d..58a53b6d9b42 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -864,7 +864,7 @@ fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port,
static __be16 farsync_type_trans(struct sk_buff *skb, struct net_device *dev)
{
skb->dev = dev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->pkt_type = PACKET_HOST;
return htons(ETH_P_CUST);
}
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
index c9664fd8a917..00e0aaadabcc 100644
--- a/drivers/net/wan/hdlc_cisco.c
+++ b/drivers/net/wan/hdlc_cisco.c
@@ -124,7 +124,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
skb_put(skb, sizeof(struct cisco_packet));
skb->priority = TC_PRIO_CONTROL;
skb->dev = dev;
- skb->nh.raw = skb->data;
+ skb_reset_network_header(skb);
dev_queue_xmit(skb);
}
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index c6c3c757d6f1..aeb2789adf26 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -533,7 +533,7 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
skb->protocol = __constant_htons(NLPID_CCITT_ANSI_LMI);
fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI);
}
- data = skb->tail;
+ data = skb_tail_pointer(skb);
data[i++] = LMI_CALLREF;
data[i++] = dce ? LMI_STATUS : LMI_STATUS_ENQUIRY;
if (lmi == LMI_ANSI)
@@ -590,7 +590,7 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
skb_put(skb, i);
skb->priority = TC_PRIO_CONTROL;
skb->dev = dev;
- skb->nh.raw = skb->data;
+ skb_reset_network_header(skb);
dev_queue_xmit(skb);
}
@@ -1011,7 +1011,6 @@ static int fr_rx(struct sk_buff *skb)
stats->rx_bytes += skb->len;
if (pvc->state.becn)
stats->rx_compressed++;
- skb->dev = dev;
netif_rx(skb);
return NET_RX_SUCCESS;
} else {
diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c
index a02c5fb40567..9ba3e4ee6ec7 100644
--- a/drivers/net/wan/hostess_sv11.c
+++ b/drivers/net/wan/hostess_sv11.c
@@ -59,7 +59,7 @@ static void hostess_input(struct z8530_channel *c, struct sk_buff *skb)
/* Drop the CRC - it's not a good idea to try and negotiate it ;) */
skb_trim(skb, skb->len-2);
skb->protocol=__constant_htons(ETH_P_WAN_PPP);
- skb->mac.raw=skb->data;
+ skb_reset_mac_header(skb);
skb->dev=c->netdevice;
/*
* Send it to the PPP layer. We don't have time to process
diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c
index 2b54f1bc3a0d..ae132c1c5459 100644
--- a/drivers/net/wan/lmc/lmc_main.c
+++ b/drivers/net/wan/lmc/lmc_main.c
@@ -1636,7 +1636,7 @@ static int lmc_rx (struct net_device *dev) /*fold00*/
if (nsb) {
sc->lmc_rxq[i] = nsb;
nsb->dev = dev;
- sc->lmc_rxring[i].buffer1 = virt_to_bus (nsb->tail);
+ sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb));
}
sc->failed_recv_alloc = 1;
goto skip_packet;
@@ -1667,8 +1667,8 @@ static int lmc_rx (struct net_device *dev) /*fold00*/
skb_put (skb, len);
skb->protocol = lmc_proto_type(sc, skb);
skb->protocol = htons(ETH_P_WAN_PPP);
- skb->mac.raw = skb->data;
-// skb->nh.raw = skb->data;
+ skb_reset_mac_header(skb);
+ /* skb_reset_network_header(skb); */
skb->dev = dev;
lmc_proto_netif(sc, skb);
@@ -1679,7 +1679,7 @@ static int lmc_rx (struct net_device *dev) /*fold00*/
if (nsb) {
sc->lmc_rxq[i] = nsb;
nsb->dev = dev;
- sc->lmc_rxring[i].buffer1 = virt_to_bus (nsb->tail);
+ sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb));
/* Transferred to 21140 below */
}
else {
@@ -1702,11 +1702,11 @@ static int lmc_rx (struct net_device *dev) /*fold00*/
if(!nsb) {
goto give_it_anyways;
}
- memcpy(skb_put(nsb, len), skb->data, len);
+ skb_copy_from_linear_data(skb, skb_put(nsb, len), len);
nsb->protocol = lmc_proto_type(sc, skb);
- nsb->mac.raw = nsb->data;
-// nsb->nh.raw = nsb->data;
+ skb_reset_mac_header(nsb);
+ /* skb_reset_network_header(nsb); */
nsb->dev = dev;
lmc_proto_netif(sc, nsb);
}
@@ -1932,7 +1932,7 @@ static void lmc_softreset (lmc_softc_t * const sc) /*fold00*/
sc->lmc_rxring[i].status = 0x80000000;
/* used to be PKT_BUF_SZ now uses skb since we lose some to head room */
- sc->lmc_rxring[i].length = skb->end - skb->data;
+ sc->lmc_rxring[i].length = skb_tailroom(skb);
/* use to be tail which is dumb since you're thinking why write
* to the end of the packj,et but since there's nothing there tail == data
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index 62184dee377c..999bf71937ca 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -1755,17 +1755,17 @@ cpc_trace(struct net_device *dev, struct sk_buff *skb_main, char rx_tx)
skb->dev = dev;
skb->protocol = htons(ETH_P_CUST);
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->pkt_type = PACKET_HOST;
skb->len = 10 + skb_main->len;
- memcpy(skb->data, dev->name, 5);
+ skb_copy_to_linear_data(skb, dev->name, 5);
skb->data[5] = '[';
skb->data[6] = rx_tx;
skb->data[7] = ']';
skb->data[8] = ':';
skb->data[9] = ' ';
- memcpy(&skb->data[10], skb_main->data, skb_main->len);
+ skb_copy_from_linear_data(skb_main, &skb->data[10], skb_main->len);
netif_rx(skb);
}
diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c
index 5873c346e7e9..07dbdfbfc15d 100644
--- a/drivers/net/wan/pc300_tty.c
+++ b/drivers/net/wan/pc300_tty.c
@@ -1003,17 +1003,17 @@ static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx)
skb_put (skb, 10 + len);
skb->dev = dev->dev;
skb->protocol = htons(ETH_P_CUST);
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->pkt_type = PACKET_HOST;
skb->len = 10 + len;
- memcpy(skb->data,dev->dev->name,5);
+ skb_copy_to_linear_data(skb, dev->dev->name, 5);
skb->data[5] = '[';
skb->data[6] = rxtx;
skb->data[7] = ']';
skb->data[8] = ':';
skb->data[9] = ' ';
- memcpy(&skb->data[10], buf, len);
+ skb_copy_to_linear_data_offset(skb, 10, buf, len);
netif_rx(skb);
}
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index fc5c0c611ffd..35eded7ffb2d 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -999,11 +999,6 @@ get_rx_buf( struct net_device *dev )
if( !skb )
return NULL;
-#ifdef CONFIG_SBNI_MULTILINE
- skb->dev = ((struct net_local *) dev->priv)->master;
-#else
- skb->dev = dev;
-#endif
skb_reserve( skb, 2 ); /* Align IP on longword boundaries */
return skb;
}
diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c
index 70fb1b98b1dd..131358108c5a 100644
--- a/drivers/net/wan/sealevel.c
+++ b/drivers/net/wan/sealevel.c
@@ -61,7 +61,7 @@ static void sealevel_input(struct z8530_channel *c, struct sk_buff *skb)
/* Drop the CRC - it's not a good idea to try and negotiate it ;) */
skb_trim(skb, skb->len-2);
skb->protocol=htons(ETH_P_WAN_PPP);
- skb->mac.raw=skb->data;
+ skb_reset_mac_header(skb);
skb->dev=c->netdevice;
/*
* Send it to the PPP layer. We don't have time to process
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index 218f7b574ab3..67fc67cfd452 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -227,7 +227,7 @@ static void sppp_input (struct net_device *dev, struct sk_buff *skb)
unsigned long flags;
skb->dev=dev;
- skb->mac.raw=skb->data;
+ skb_reset_mac_header(skb);
if (dev->flags & IFF_RUNNING)
{
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 8b4540bfc1b0..98ef400908b8 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -1656,7 +1656,7 @@ static void z8530_rx_done(struct z8530_channel *c)
else
{
skb_put(skb, ct);
- memcpy(skb->data, rxb, ct);
+ skb_copy_to_linear_data(skb, rxb, ct);
c->stats.rx_packets++;
c->stats.rx_bytes+=ct;
}
@@ -1782,7 +1782,7 @@ int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb)
*/
c->tx_next_ptr=c->tx_dma_buf[c->tx_dma_used];
c->tx_dma_used^=1; /* Flip temp buffer */
- memcpy(c->tx_next_ptr, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, c->tx_next_ptr, skb->len);
}
else
c->tx_next_ptr=skb->data;
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index ece3d9c2dc61..4426841b2be6 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -2,47 +2,21 @@
# Wireless LAN device configuration
#
-menu "Wireless LAN (non-hamradio)"
- depends on NETDEVICES
-
-config NET_RADIO
- bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions"
- select WIRELESS_EXT
- ---help---
- Support for wireless LANs and everything having to do with radio,
- but not with amateur radio or FM broadcasting.
-
- Saying Y here also enables the Wireless Extensions (creates
- /proc/net/wireless and enables iwconfig access). The Wireless
- Extension is a generic API allowing a driver to expose to the user
- space configuration and statistics specific to common Wireless LANs.
- The beauty of it is that a single set of tool can support all the
- variations of Wireless LANs, regardless of their type (as long as
- the driver supports Wireless Extension). Another advantage is that
- these parameters may be changed on the fly without restarting the
- driver (or Linux). If you wish to use Wireless Extensions with
- wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch
- the tools from
- <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+menu "Wireless LAN"
-config NET_WIRELESS_RTNETLINK
- bool "Wireless Extension API over RtNetlink"
- depends on NET_RADIO
+config WLAN_PRE80211
+ bool "Wireless LAN (pre-802.11)"
+ depends on NETDEVICES
---help---
- Support the Wireless Extension API over the RtNetlink socket
- in addition to the traditional ioctl interface (selected above).
+ Say Y if you have any pre-802.11 wireless LAN hardware.
- For now, few tools use this facility, but it might grow in the
- future. The only downside is that it adds 4.5 kB to your kernel.
-
-# Note : the cards are obsolete (can't buy them anymore), but the drivers
-# are not, as people are still using them...
-comment "Obsolete Wireless cards support (pre-802.11)"
- depends on NET_RADIO && (INET || ISA || PCMCIA)
+ This option does not affect the kernel build, it only
+ lets you choose drivers.
config STRIP
tristate "STRIP (Metricom starmode radio IP)"
- depends on NET_RADIO && INET
+ depends on INET && WLAN_PRE80211
+ select WIRELESS_EXT
---help---
Say Y if you have a Metricom radio and intend to use Starmode Radio
IP. STRIP is a radio protocol developed for the MosquitoNet project
@@ -65,7 +39,8 @@ config STRIP
config ARLAN
tristate "Aironet Arlan 655 & IC2200 DS support"
- depends on NET_RADIO && ISA && !64BIT
+ depends on ISA && !64BIT && WLAN_PRE80211
+ select WIRELESS_EXT
---help---
Aironet makes Arlan, a class of wireless LAN adapters. These use the
www.Telxon.com chip, which is also used on several similar cards.
@@ -80,7 +55,8 @@ config ARLAN
config WAVELAN
tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support"
- depends on NET_RADIO && ISA
+ depends on ISA && WLAN_PRE80211
+ select WIRELESS_EXT
---help---
The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is
a Radio LAN (wireless Ethernet-like Local Area Network) using the
@@ -107,7 +83,8 @@ config WAVELAN
config PCMCIA_WAVELAN
tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support"
- depends on NET_RADIO && PCMCIA
+ depends on PCMCIA && WLAN_PRE80211
+ select WIRELESS_EXT
help
Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA
(PC-card) wireless Ethernet networking card to your computer. This
@@ -118,7 +95,8 @@ config PCMCIA_WAVELAN
config PCMCIA_NETWAVE
tristate "Xircom Netwave AirSurfer Pcmcia wireless support"
- depends on NET_RADIO && PCMCIA
+ depends on PCMCIA && WLAN_PRE80211
+ select WIRELESS_EXT
help
Say Y here if you intend to attach this type of PCMCIA (PC-card)
wireless Ethernet networking card to your computer.
@@ -126,12 +104,20 @@ config PCMCIA_NETWAVE
To compile this driver as a module, choose M here: the module will be
called netwave_cs. If unsure, say N.
-comment "Wireless 802.11 Frequency Hopping cards support"
- depends on NET_RADIO && PCMCIA
+
+config WLAN_80211
+ bool "Wireless LAN (IEEE 802.11)"
+ depends on NETDEVICES
+ ---help---
+ Say Y if you have any 802.11 wireless LAN hardware.
+
+ This option does not affect the kernel build, it only
+ lets you choose drivers.
config PCMCIA_RAYCS
tristate "Aviator/Raytheon 2.4MHz wireless support"
- depends on NET_RADIO && PCMCIA
+ depends on PCMCIA && WLAN_80211
+ select WIRELESS_EXT
---help---
Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
(PC-card) wireless Ethernet networking card to your computer.
@@ -141,12 +127,10 @@ config PCMCIA_RAYCS
To compile this driver as a module, choose M here: the module will be
called ray_cs. If unsure, say N.
-comment "Wireless 802.11b ISA/PCI cards support"
- depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
-
config IPW2100
tristate "Intel PRO/Wireless 2100 Network Connection"
- depends on NET_RADIO && PCI
+ depends on PCI && WLAN_80211
+ select WIRELESS_EXT
select FW_LOADER
select IEEE80211
---help---
@@ -200,7 +184,8 @@ config IPW2100_DEBUG
config IPW2200
tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
- depends on NET_RADIO && PCI
+ depends on PCI && WLAN_80211
+ select WIRELESS_EXT
select FW_LOADER
select IEEE80211
---help---
@@ -282,7 +267,8 @@ config IPW2200_DEBUG
config AIRO
tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
- depends on NET_RADIO && ISA_DMA_API && (PCI || BROKEN)
+ depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN)
+ select WIRELESS_EXT
select CRYPTO
---help---
This is the standard Linux driver to support Cisco/Aironet ISA and
@@ -299,7 +285,8 @@ config AIRO
config HERMES
tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
- depends on NET_RADIO && (PPC_PMAC || PCI || PCMCIA)
+ depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211
+ select WIRELESS_EXT
---help---
A driver for 802.11b wireless cards based on the "Hermes" or
Intersil HFA384x (Prism 2) MAC controller. This includes the vast
@@ -373,7 +360,8 @@ config PCI_HERMES
config ATMEL
tristate "Atmel at76c50x chipset 802.11b support"
- depends on NET_RADIO && (PCI || PCMCIA)
+ depends on (PCI || PCMCIA) && WLAN_80211
+ select WIRELESS_EXT
select FW_LOADER
select CRC32
---help---
@@ -394,13 +382,9 @@ config PCI_ATMEL
Enable support for PCI and mini-PCI cards containing the
Atmel at76c506 chip.
-# If Pcmcia is compiled in, offer Pcmcia cards...
-comment "Wireless 802.11b Pcmcia/Cardbus cards support"
- depends on NET_RADIO && PCMCIA
-
config PCMCIA_HERMES
tristate "Hermes PCMCIA card support"
- depends on NET_RADIO && PCMCIA && HERMES
+ depends on PCMCIA && HERMES
---help---
A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -420,7 +404,7 @@ config PCMCIA_HERMES
config PCMCIA_SPECTRUM
tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
- depends on NET_RADIO && PCMCIA && HERMES
+ depends on PCMCIA && HERMES
select FW_LOADER
---help---
@@ -434,7 +418,8 @@ config PCMCIA_SPECTRUM
config AIRO_CS
tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
- depends on NET_RADIO && PCMCIA && (BROKEN || !M32R)
+ depends on PCMCIA && (BROKEN || !M32R) && WLAN_80211
+ select WIRELESS_EXT
select CRYPTO
select CRYPTO_AES
---help---
@@ -458,7 +443,8 @@ config AIRO_CS
config PCMCIA_ATMEL
tristate "Atmel at76c502/at76c504 PCMCIA cards"
- depends on NET_RADIO && ATMEL && PCMCIA
+ depends on ATMEL && PCMCIA
+ select WIRELESS_EXT
select FW_LOADER
select CRC32
---help---
@@ -467,17 +453,17 @@ config PCMCIA_ATMEL
config PCMCIA_WL3501
tristate "Planet WL3501 PCMCIA cards"
- depends on NET_RADIO && EXPERIMENTAL && PCMCIA
+ depends on EXPERIMENTAL && PCMCIA && WLAN_80211
+ select WIRELESS_EXT
---help---
A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet.
It has basic support for Linux wireless extensions and initial
micro support for ethtool.
-comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support"
- depends on NET_RADIO && PCI
config PRISM54
tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus'
- depends on PCI && NET_RADIO && EXPERIMENTAL
+ depends on PCI && EXPERIMENTAL && WLAN_80211
+ select WIRELESS_EXT
select FW_LOADER
---help---
Enable PCI and Cardbus support for the following chipset based cards:
@@ -523,7 +509,8 @@ config PRISM54
config USB_ZD1201
tristate "USB ZD1201 based Wireless device support"
- depends on USB && NET_RADIO
+ depends on USB && WLAN_80211
+ select WIRELESS_EXT
select FW_LOADER
---help---
Say Y if you want to use wireless LAN adapters based on the ZyDAS
@@ -542,11 +529,4 @@ source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/bcm43xx/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
-# yes, this works even when no drivers are selected
-config NET_WIRELESS
- bool
- depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA)
- default y
-
endmenu
-
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 2ada76a93cb6..7fe0a61091a6 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -2444,7 +2444,7 @@ static int add_airo_dev( struct net_device *dev );
static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
{
- memcpy(haddr, skb->mac.raw + 10, ETH_ALEN);
+ memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
return ETH_ALEN;
}
@@ -3411,14 +3411,12 @@ badrx:
OUT4500( apriv, EVACK, EV_RX);
if (test_bit(FLAG_802_11, &apriv->flags)) {
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->pkt_type = PACKET_OTHERHOST;
skb->dev = apriv->wifidev;
skb->protocol = htons(ETH_P_802_2);
- } else {
- skb->dev = dev;
+ } else
skb->protocol = eth_type_trans(skb,dev);
- }
skb->dev->last_rx = jiffies;
skb->ip_summed = CHECKSUM_NONE;
@@ -3641,7 +3639,6 @@ badmic:
}
#endif /* WIRELESS_SPY */
- skb->dev = ai->dev;
skb->ip_summed = CHECKSUM_NONE;
skb->protocol = eth_type_trans(skb, ai->dev);
skb->dev->last_rx = jiffies;
@@ -3749,7 +3746,7 @@ void mpi_receive_802_11 (struct airo_info *ai)
wireless_spy_update(ai->dev, sa, &wstats);
}
#endif /* IW_WIRELESS_SPY */
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->pkt_type = PACKET_OTHERHOST;
skb->dev = ai->wifidev;
skb->protocol = htons(ETH_P_802_2);
diff --git a/drivers/net/wireless/arlan-main.c b/drivers/net/wireless/arlan-main.c
index 4688e56b69c7..498e8486d125 100644
--- a/drivers/net/wireless/arlan-main.c
+++ b/drivers/net/wireless/arlan-main.c
@@ -1500,7 +1500,6 @@ static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short
break;
}
skb_reserve(skb, 2);
- skb->dev = dev;
skbtmp = skb_put(skb, pkt_len);
memcpy_fromio(skbtmp + ARLAN_FAKE_HDR_LEN, ((char __iomem *) arlan) + rxOffset, pkt_len - ARLAN_FAKE_HDR_LEN);
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 23eba698aec5..51a7db53afa5 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -827,14 +827,14 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
if (priv->wep_is_on)
frame_ctl |= IEEE80211_FCTL_PROTECTED;
if (priv->operating_mode == IW_MODE_ADHOC) {
- memcpy(&header.addr1, skb->data, 6);
+ skb_copy_from_linear_data(skb, &header.addr1, 6);
memcpy(&header.addr2, dev->dev_addr, 6);
memcpy(&header.addr3, priv->BSSID, 6);
} else {
frame_ctl |= IEEE80211_FCTL_TODS;
memcpy(&header.addr1, priv->CurrentBSSID, 6);
memcpy(&header.addr2, dev->dev_addr, 6);
- memcpy(&header.addr3, skb->data, 6);
+ skb_copy_from_linear_data(skb, &header.addr3, 6);
}
if (priv->use_wpa)
@@ -920,7 +920,6 @@ static void fast_rx_path(struct atmel_private *priv,
memcpy(&skbp[6], header->addr2, 6); /* source address */
priv->dev->last_rx = jiffies;
- skb->dev = priv->dev;
skb->protocol = eth_type_trans(skb, priv->dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
@@ -1028,7 +1027,6 @@ static void frag_rx_path(struct atmel_private *priv,
priv->rx_buf,
priv->frag_len + 12);
priv->dev->last_rx = jiffies;
- skb->dev = priv->dev;
skb->protocol = eth_type_trans(skb, priv->dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
diff --git a/drivers/net/wireless/bcm43xx/Kconfig b/drivers/net/wireless/bcm43xx/Kconfig
index 533993f538fc..ce397e4284f4 100644
--- a/drivers/net/wireless/bcm43xx/Kconfig
+++ b/drivers/net/wireless/bcm43xx/Kconfig
@@ -1,6 +1,7 @@
config BCM43XX
tristate "Broadcom BCM43xx wireless support"
- depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL
+ depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL
+ select WIRELESS_EXT
select FW_LOADER
select HW_RANDOM
---help---
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c
index 6e0dc76400e5..e3d2e61a31ee 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c
@@ -998,7 +998,8 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring,
assert(0);
return;
}
- memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len);
+ skb_copy_from_linear_data(skb, skb_put(bounce_skb, skb->len),
+ skb->len);
dev_kfree_skb_any(skb);
skb = bounce_skb;
}
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig
index 308f773ad566..1fef33169fdd 100644
--- a/drivers/net/wireless/hostap/Kconfig
+++ b/drivers/net/wireless/hostap/Kconfig
@@ -1,6 +1,7 @@
config HOSTAP
tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
- depends on NET_RADIO
+ depends on WLAN_80211
+ select WIRELESS_EXT
select IEEE80211
select IEEE80211_CRYPT_WEP
---help---
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c
index 7e04dc94b3bc..cbedc9ee740a 100644
--- a/drivers/net/wireless/hostap/hostap_80211_rx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_rx.c
@@ -167,7 +167,7 @@ hdr->f.status = s; hdr->f.len = l; hdr->f.data = d
ret = skb->len - phdrlen;
skb->dev = dev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, hdrlen);
if (prism_header)
skb_pull(skb, phdrlen);
@@ -933,12 +933,14 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
if (frag == 0) {
/* copy first fragment (including full headers) into
* beginning of the fragment cache skb */
- memcpy(skb_put(frag_skb, flen), skb->data, flen);
+ skb_copy_from_linear_data(skb, skb_put(frag_skb, flen),
+ flen);
} else {
/* append frame payload to the end of the fragment
* cache skb */
- memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
- flen);
+ skb_copy_from_linear_data_offset(skb, hdrlen,
+ skb_put(frag_skb,
+ flen), flen);
}
dev_kfree_skb(skb);
skb = NULL;
@@ -1044,8 +1046,9 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
skb->len >= ETH_HLEN + ETH_ALEN) {
/* Non-standard frame: get addr4 from its bogus location after
* the payload */
- memcpy(skb->data + ETH_ALEN,
- skb->data + skb->len - ETH_ALEN, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, skb->len - ETH_ALEN,
+ skb->data + ETH_ALEN,
+ ETH_ALEN);
skb_trim(skb, skb->len - ETH_ALEN);
}
@@ -1073,17 +1076,17 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
if (skb2 != NULL) {
/* send to wireless media */
- skb2->protocol = __constant_htons(ETH_P_802_3);
- skb2->mac.raw = skb2->nh.raw = skb2->data;
- /* skb2->nh.raw = skb2->data + ETH_HLEN; */
skb2->dev = dev;
+ skb2->protocol = __constant_htons(ETH_P_802_3);
+ skb_reset_mac_header(skb2);
+ skb_reset_network_header(skb2);
+ /* skb2->network_header += ETH_HLEN; */
dev_queue_xmit(skb2);
}
if (skb) {
skb->protocol = eth_type_trans(skb, dev);
memset(skb->cb, 0, sizeof(skb->cb));
- skb->dev = dev;
netif_rx(skb);
}
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c
index 4a5be70c0419..246fac0e8001 100644
--- a/drivers/net/wireless/hostap/hostap_80211_tx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_tx.c
@@ -146,7 +146,8 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev)
fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
/* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA,
* Addr4 = SA */
- memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, ETH_ALEN,
+ &hdr.addr4, ETH_ALEN);
hdr_len += ETH_ALEN;
} else {
/* bogus 4-addr format to workaround Prism2 station
@@ -159,7 +160,8 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* SA from skb->data + ETH_ALEN will be added after
* frame payload; use hdr.addr4 as a temporary buffer
*/
- memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, ETH_ALEN,
+ &hdr.addr4, ETH_ALEN);
need_tailroom += ETH_ALEN;
}
@@ -174,24 +176,27 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev)
else
memcpy(&hdr.addr1, local->bssid, ETH_ALEN);
memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(&hdr.addr3, skb->data, ETH_ALEN);
+ skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN);
} else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) {
fc |= IEEE80211_FCTL_FROMDS;
/* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */
- memcpy(&hdr.addr1, skb->data, ETH_ALEN);
+ skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN);
memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr3,
+ ETH_ALEN);
} else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) {
fc |= IEEE80211_FCTL_TODS;
/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
memcpy(&hdr.addr1, to_assoc_ap ?
local->assoc_ap_addr : local->bssid, ETH_ALEN);
- memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(&hdr.addr3, skb->data, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2,
+ ETH_ALEN);
+ skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN);
} else if (local->iw_mode == IW_MODE_ADHOC) {
/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
- memcpy(&hdr.addr1, skb->data, ETH_ALEN);
- memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2,
+ ETH_ALEN);
memcpy(&hdr.addr3, local->bssid, ETH_ALEN);
}
@@ -237,7 +242,7 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev)
iface->stats.tx_packets++;
iface->stats.tx_bytes += skb->len;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
meta = (struct hostap_skb_tx_data *) skb->cb;
memset(meta, 0, sizeof(*meta));
meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
index efb8cf3bd8ad..4ca8a27b8c55 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/hostap/hostap_ap.c
@@ -982,7 +982,8 @@ static void prism2_send_mgmt(struct net_device *dev,
meta->tx_cb_idx = tx_cb_idx;
skb->dev = dev;
- skb->mac.raw = skb->nh.raw = skb->data;
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
dev_queue_xmit(skb);
}
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
@@ -1276,8 +1277,8 @@ static char * ap_auth_make_challenge(struct ap_data *ap)
return NULL;
}
- memcpy(tmpbuf, skb->data + ap->crypt->extra_mpdu_prefix_len,
- WLAN_AUTH_CHALLENGE_LEN);
+ skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len,
+ tmpbuf, WLAN_AUTH_CHALLENGE_LEN);
dev_kfree_skb(skb);
return tmpbuf;
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c
index 3079378fb8cd..fb01fb95a9f0 100644
--- a/drivers/net/wireless/hostap/hostap_hw.c
+++ b/drivers/net/wireless/hostap/hostap_hw.c
@@ -1838,13 +1838,14 @@ static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev)
/* skb->data starts with txdesc->frame_control */
hdr_len = 24;
- memcpy(&txdesc.frame_control, skb->data, hdr_len);
+ skb_copy_from_linear_data(skb, &txdesc.frame_control, hdr_len);
fc = le16_to_cpu(txdesc.frame_control);
if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
(fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS) &&
skb->len >= 30) {
/* Addr4 */
- memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, hdr_len, txdesc.addr4,
+ ETH_ALEN);
hdr_len += ETH_ALEN;
}
@@ -2217,7 +2218,7 @@ static void hostap_tx_callback(local_info_t *local,
memcpy(skb_put(skb, len), payload, len);
skb->dev = local->dev;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
cb->func(skb, ok, cb->data);
}
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c
index 9077e6edde34..1f9edd91565d 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/hostap/hostap_main.c
@@ -590,20 +590,20 @@ void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx)
int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr)
{
- memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */
+ memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
return ETH_ALEN;
}
int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr)
{
- if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) {
- memcpy(haddr, skb->mac.raw +
- sizeof(struct linux_wlan_ng_prism_hdr) + 10,
+ const unsigned char *mac = skb_mac_header(skb);
+
+ if (*(u32 *)mac == LWNG_CAP_DID_BASE) {
+ memcpy(haddr, mac + sizeof(struct linux_wlan_ng_prism_hdr) + 10,
ETH_ALEN); /* addr2 */
- } else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */
- memcpy(haddr, skb->mac.raw +
- sizeof(struct linux_wlan_ng_cap_hdr) + 10,
+ } else { /* (*(u32 *)mac == htonl(LWNG_CAPHDR_VERSION)) */
+ memcpy(haddr, mac + sizeof(struct linux_wlan_ng_cap_hdr) + 10,
ETH_ALEN); /* addr2 */
}
return ETH_ALEN;
@@ -1063,7 +1063,8 @@ int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype,
meta->iface = netdev_priv(dev);
skb->dev = dev;
- skb->mac.raw = skb->nh.raw = skb->data;
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
dev_queue_xmit(skb);
return 0;
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
index ad6e4a428355..9137a4dd02eb 100644
--- a/drivers/net/wireless/ipw2100.c
+++ b/drivers/net/wireless/ipw2100.c
@@ -2416,8 +2416,9 @@ static void isr_rx(struct ipw2100_priv *priv, int i,
#ifdef IPW2100_RX_DEBUG
/* Make a copy of the frame so we can dump it to the logs if
* ieee80211_rx fails */
- memcpy(packet_data, packet->skb->data,
- min_t(u32, status->frame_size, IPW_RX_NIC_BUFFER_LENGTH));
+ skb_copy_from_linear_data(packet->skb, packet_data,
+ min_t(u32, status->frame_size,
+ IPW_RX_NIC_BUFFER_LENGTH));
#endif
if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index c878a2f3239c..4839a45098cb 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -8133,7 +8133,7 @@ static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
skb->dev = priv->ieee->dev;
/* Point raw at the ieee80211_stats */
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = __constant_htons(ETH_P_80211_STATS);
@@ -10355,7 +10355,7 @@ static void ipw_handle_promiscuous_tx(struct ipw_priv *priv,
rt_hdr->it_len = dst->len;
- memcpy(skb_put(dst, len), src->data, len);
+ skb_copy_from_linear_data(src, skb_put(dst, len), len);
if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats))
dev_kfree_skb_any(dst);
diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c
index a009ab517710..45b00e13ab2b 100644
--- a/drivers/net/wireless/netwave_cs.c
+++ b/drivers/net/wireless/netwave_cs.c
@@ -1283,7 +1283,6 @@ static int netwave_rx(struct net_device *dev)
skb_reserve( skb, 2); /* Align IP on 16 byte */
skb_put( skb, rcvLen);
- skb->dev = dev;
/* Copy packet fragments to the skb data area */
ptr = (u_char*) skb->data;
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 4e7f6cf51436..062286dc8e15 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -689,7 +689,7 @@ static void orinoco_stat_gather(struct net_device *dev,
/* Note : gcc will optimise the whole section away if
* WIRELESS_SPY is not defined... - Jean II */
if (SPY_NUMBER(priv)) {
- orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
+ orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN,
desc->signal, desc->silence);
}
}
@@ -770,7 +770,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
/* Copy the 802.11 header to the skb */
memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
/* If any, copy the data from the card to the skb */
if (datalen > 0) {
@@ -915,7 +915,6 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
memcpy(hdr->h_source, desc.addr2, ETH_ALEN);
dev->last_rx = jiffies;
- skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE;
if (fc & IEEE80211_FCTL_TODS)
diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c
index b1122912ee2d..dd070cccf324 100644
--- a/drivers/net/wireless/prism54/islpci_eth.c
+++ b/drivers/net/wireless/prism54/islpci_eth.c
@@ -136,7 +136,7 @@ islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev)
printk("islpci_eth_transmit:wds_mac\n");
#endif
memmove(skb->data + 6, src, skb->len);
- memcpy(skb->data, wds_mac, 6);
+ skb_copy_to_linear_data(skb, wds_mac, 6);
} else {
memmove(skb->data, src, skb->len);
}
@@ -162,13 +162,16 @@ islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev)
skb_put(newskb, init_wds ? skb->len + 6 : skb->len);
if (init_wds) {
- memcpy(newskb->data + 6, skb->data, skb->len);
- memcpy(newskb->data, wds_mac, 6);
+ skb_copy_from_linear_data(skb,
+ newskb->data + 6,
+ skb->len);
+ skb_copy_to_linear_data(newskb, wds_mac, 6);
#ifdef ISLPCI_ETH_DEBUG
printk("islpci_eth_transmit:wds_mac\n");
#endif
} else
- memcpy(newskb->data, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, newskb->data,
+ skb->len);
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n",
@@ -303,7 +306,7 @@ islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb)
skb_pull(*skb, sizeof (struct rfmon_header));
(*skb)->protocol = htons(ETH_P_802_2);
- (*skb)->mac.raw = (*skb)->data;
+ skb_reset_mac_header(*skb);
(*skb)->pkt_type = PACKET_OTHERHOST;
return 0;
@@ -374,10 +377,6 @@ islpci_eth_receive(islpci_private *priv)
DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
display_buffer((char *) skb->data, skb->len);
#endif
-
- /* do some additional sk_buff and network layer parameters */
- skb->dev = ndev;
-
/* take care of monitor mode and spy monitoring. */
if (unlikely(priv->iw_mode == IW_MODE_MONITOR))
discard = islpci_monitor_rx(priv, &skb);
@@ -398,8 +397,10 @@ islpci_eth_receive(islpci_private *priv)
/* Update spy records */
wireless_spy_update(ndev, annex->addr2, &wstats);
- memcpy(skb->data + sizeof (struct rfmon_header),
- skb->data, 2 * ETH_ALEN);
+ skb_copy_from_linear_data(skb,
+ (skb->data +
+ sizeof(struct rfmon_header)),
+ 2 * ETH_ALEN);
skb_pull(skb, sizeof (struct rfmon_header));
}
skb->protocol = eth_type_trans(skb, ndev);
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 47b2ccb6a633..3be624295a1f 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -2232,7 +2232,6 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, unsigned i
return;
}
skb_reserve( skb, 2); /* Align IP on 16 byte (TBD check this)*/
- skb->dev = dev;
DEBUG(4,"ray_cs rx_data total_len = %x, rx_len = %x\n",total_len,rx_len);
@@ -2243,7 +2242,8 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, unsigned i
rx_ptr += copy_from_rx_buff(local, rx_ptr, pkt_addr & RX_BUFF_END, rx_len);
/* Get source address */
#ifdef WIRELESS_SPY
- memcpy(linksrcaddr, ((struct mac_header *)skb->data)->addr_2, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, offsetof(struct mac_header, addr_2),
+ linksrcaddr, ETH_ALEN);
#endif
/* Now, deal with encapsulation/translation/sniffer */
if (!sniffer) {
diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c
index f5ce1c6063d8..2a299a0676a6 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/net/wireless/strip.c
@@ -2009,7 +2009,7 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header * header,
packetlen);
skb->dev = get_strip_dev(strip_info);
skb->protocol = header->protocol;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
/* Having put a fake header on the front of the sk_buff for the */
/* benefit of tools like tcpdump, skb_pull now 'consumes' that */
diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c
index 2aa3c761dd83..1cf090d60edc 100644
--- a/drivers/net/wireless/wavelan.c
+++ b/drivers/net/wireless/wavelan.c
@@ -2512,14 +2512,13 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
return;
}
- skb->dev = dev;
-
/* Copy the packet to the buffer. */
obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize);
skb->protocol = eth_type_trans(skb, dev);
#ifdef DEBUG_RX_INFO
- wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+ wv_packet_info(skb_mac_header(skb), sksize, dev->name,
+ "wv_packet_read");
#endif /* DEBUG_RX_INFO */
/* Statistics-gathering and associated stuff.
@@ -2555,7 +2554,7 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
/* Spying stuff */
#ifdef IW_WIRELESS_SPY
- wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE,
+ wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE,
stats);
#endif /* IW_WIRELESS_SPY */
#ifdef HISTOGRAM
@@ -2939,7 +2938,7 @@ static int wavelan_packet_xmit(struct sk_buff *skb, struct net_device * dev)
* need to pad. Jean II */
if (skb->len < ETH_ZLEN) {
memset(data, 0, ETH_ZLEN);
- memcpy(data, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, data, skb->len);
/* Write packet on the card */
if(wv_packet_write(dev, data, ETH_ZLEN))
return 1; /* We failed */
diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c
index b04239792f63..67b867f837ca 100644
--- a/drivers/net/wireless/wavelan_cs.c
+++ b/drivers/net/wireless/wavelan_cs.c
@@ -2884,14 +2884,12 @@ wv_packet_read(struct net_device * dev,
return;
}
- skb->dev = dev;
-
skb_reserve(skb, 2);
fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize);
skb->protocol = eth_type_trans(skb, dev);
#ifdef DEBUG_RX_INFO
- wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+ wv_packet_info(skb_mac_header(skb), sksize, dev->name, "wv_packet_read");
#endif /* DEBUG_RX_INFO */
/* Statistics gathering & stuff associated.
@@ -2925,7 +2923,7 @@ wv_packet_read(struct net_device * dev,
#endif /* WAVELAN_ROAMING */
#ifdef WIRELESS_SPY
- wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
+ wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, stats);
#endif /* WIRELESS_SPY */
#ifdef HISTOGRAM
wl_his_gather(dev, stats);
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index 6cb66a356c96..935b144d9b56 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -327,7 +327,6 @@ static void zd1201_usbrx(struct urb *urb)
memcpy(skb_put(skb, 6), &data[datalen-8], 6);
memcpy(skb_put(skb, 2), &data[datalen-24], 2);
memcpy(skb_put(skb, len), data, len);
- skb->dev = zd->dev;
skb->dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, zd->dev);
zd->stats.rx_packets++;
@@ -385,7 +384,6 @@ static void zd1201_usbrx(struct urb *urb)
memcpy(skb_put(skb, 2), &data[6], 2);
memcpy(skb_put(skb, len), data+8, len);
}
- skb->dev = zd->dev;
skb->dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, zd->dev);
zd->stats.rx_packets++;
@@ -809,10 +807,10 @@ static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
txbuf[4] = 0x00;
txbuf[5] = 0x00;
- memcpy(txbuf+6, skb->data+12, skb->len-12);
+ skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12);
if (pad)
txbuf[skb->len-12+6]=0;
- memcpy(txbuf+skb->len-12+6+pad, skb->data, 12);
+ skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12);
*(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6);
txbuf[txbuflen-1] = 0;
diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig
index 66ed55bc5460..d1ab24a95630 100644
--- a/drivers/net/wireless/zd1211rw/Kconfig
+++ b/drivers/net/wireless/zd1211rw/Kconfig
@@ -1,6 +1,7 @@
config ZD1211RW
tristate "ZyDAS ZD1211/ZD1211B USB-wireless support"
- depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL
+ depends on USB && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL
+ select WIRELESS_EXT
select FW_LOADER
---help---
This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index aac8a1c5ba08..edaaad2f648b 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -62,6 +62,7 @@ static struct usb_device_id usb_ids[] = {
{ USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B },
{ USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B },
{ USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B },
/* "Driverless" devices that need ejecting */
{ USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER },
{}
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index 2412ce4917f2..3f4a7cf9efea 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -1137,7 +1137,6 @@ static int yellowfin_rx(struct net_device *dev)
skb = dev_alloc_skb(pkt_len + 2);
if (skb == NULL)
break;
- skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
eth_copy_and_sum(skb, rx_skb->data, pkt_len, 0);
skb_put(skb, pkt_len);
diff --git a/drivers/net/znet.c b/drivers/net/znet.c
index b24b0727108c..4032e9f6f9b0 100644
--- a/drivers/net/znet.c
+++ b/drivers/net/znet.c
@@ -774,7 +774,6 @@ static void znet_rx(struct net_device *dev)
znet->stats.rx_dropped++;
break;
}
- skb->dev = dev;
if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) {
int semi_cnt = (znet->rx_end - znet->rx_cur)<<1;
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index d190c05d87ed..453e6829756c 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -372,9 +372,9 @@ static __inline__ int led_get_net_activity(void)
continue;
if (LOOPBACK(in_dev->ifa_list->ifa_local))
continue;
- if (!dev->get_stats)
- continue;
stats = dev->get_stats(dev);
+ if (!stats)
+ continue;
rx_total += stats->rx_packets;
tx_total += stats->tx_packets;
}
diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c
index 9793533276ec..400bb90084cf 100644
--- a/drivers/parport/parport_sunbpp.c
+++ b/drivers/parport/parport_sunbpp.c
@@ -126,7 +126,7 @@ static unsigned char status_sunbpp_to_pc(struct parport *p)
if (!(value_tcr & P_TCR_BUSY))
bits |= PARPORT_STATUS_BUSY;
- dprintk((KERN_DEBUG "tcr 0x%x ir 0x%x\n", regs->p_tcr, regs->p_ir));
+ dprintk((KERN_DEBUG "tcr 0x%x ir 0x%x\n", value_tcr, value_ir));
dprintk((KERN_DEBUG "read status 0x%x\n", bits));
return bits;
}
@@ -147,7 +147,7 @@ static unsigned char control_sunbpp_to_pc(struct parport *p)
if (value_or & P_OR_SLCT_IN)
bits |= PARPORT_CONTROL_SELECT;
- dprintk((KERN_DEBUG "tcr 0x%x or 0x%x\n", regs->p_tcr, regs->p_or));
+ dprintk((KERN_DEBUG "tcr 0x%x or 0x%x\n", value_tcr, value_or));
dprintk((KERN_DEBUG "read control 0x%x\n", bits));
return bits;
}
@@ -165,7 +165,8 @@ static unsigned char parport_sunbpp_frob_control(struct parport *p,
unsigned char value_tcr = sbus_readb(&regs->p_tcr);
unsigned char value_or = sbus_readb(&regs->p_or);
- dprintk((KERN_DEBUG "frob1: tcr 0x%x or 0x%x\n", regs->p_tcr, regs->p_or));
+ dprintk((KERN_DEBUG "frob1: tcr 0x%x or 0x%x\n",
+ value_tcr, value_or));
if (mask & PARPORT_CONTROL_STROBE) {
if (val & PARPORT_CONTROL_STROBE) {
value_tcr &= ~P_TCR_DS;
@@ -197,7 +198,8 @@ static unsigned char parport_sunbpp_frob_control(struct parport *p,
sbus_writeb(value_or, &regs->p_or);
sbus_writeb(value_tcr, &regs->p_tcr);
- dprintk((KERN_DEBUG "frob2: tcr 0x%x or 0x%x\n", regs->p_tcr, regs->p_or));
+ dprintk((KERN_DEBUG "frob2: tcr 0x%x or 0x%x\n",
+ value_tcr, value_or));
return parport_sunbpp_read_control(p);
}
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index a3c1755b2f28..39e80fcef4b3 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -434,11 +434,6 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
drv->driver.mod_name = mod_name;
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);
@@ -574,6 +569,7 @@ struct bus_type pci_bus_type = {
static int __init pci_driver_init(void)
{
+ pci_bus_type.multithread_probe = pci_multithread_probe;
return bus_register(&pci_bus_type);
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index d3eab057b2d3..2a458279327a 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/pm.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/string.h>
@@ -891,31 +892,48 @@ pci_disable_device(struct pci_dev *dev)
}
/**
- * pci_enable_wake - enable device to generate PME# when suspended
- * @dev: - PCI device to operate on
- * @state: - Current state of device.
- * @enable: - Flag to enable or disable generation
- *
- * Set the bits in the device's PM Capabilities to generate PME# when
- * the system is suspended.
+ * pci_enable_wake - enable PCI device as wakeup event source
+ * @dev: PCI device affected
+ * @state: PCI state from which device will issue wakeup events
+ * @enable: True to enable event generation; false to disable
*
- * -EIO is returned if device doesn't have PM Capabilities.
- * -EINVAL is returned if device supports it, but can't generate wake events.
- * 0 if operation is successful.
- *
+ * This enables the device as a wakeup event source, or disables it.
+ * When such events involves platform-specific hooks, those hooks are
+ * called automatically by this routine.
+ *
+ * Devices with legacy power management (no standard PCI PM capabilities)
+ * always require such platform hooks. Depending on the platform, devices
+ * supporting the standard PCI PME# signal may require such platform hooks;
+ * they always update bits in config space to allow PME# generation.
+ *
+ * -EIO is returned if the device can't ever be a wakeup event source.
+ * -EINVAL is returned if the device can't generate wakeup events from
+ * the specified PCI state. Returns zero if the operation is successful.
*/
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
{
int pm;
+ int status;
u16 value;
+ /* Note that drivers should verify device_may_wakeup(&dev->dev)
+ * before calling this function. Platform code should report
+ * errors when drivers try to enable wakeup on devices that
+ * can't issue wakeups, or on which wakeups were disabled by
+ * userspace updating the /sys/devices.../power/wakeup file.
+ */
+
+ status = call_platform_enable_wakeup(&dev->dev, enable);
+
/* find PCI PM capability in list */
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
- /* If device doesn't support PM Capabilities, but request is to disable
- * wake events, it's a nop; otherwise fail */
- if (!pm)
- return enable ? -EIO : 0;
+ /* If device doesn't support PM Capabilities, but caller wants to
+ * disable wake events, it's a NOP. Otherwise fail unless the
+ * platform hooks handled this legacy device already.
+ */
+ if (!pm)
+ return enable ? status : 0;
/* Check device's ability to generate PME# */
pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
@@ -924,8 +942,14 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */
/* Check if it can generate PME# from requested state. */
- if (!value || !(value & (1 << state)))
+ if (!value || !(value & (1 << state))) {
+ /* if it can't, revert what the platform hook changed,
+ * always reporting the base "EINVAL, can't PME#" error
+ */
+ if (enable)
+ call_platform_enable_wakeup(&dev->dev, 0);
return enable ? -EINVAL : 0;
+ }
pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
@@ -936,7 +960,7 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
value &= ~PCI_PM_CTRL_PME_ENABLE;
pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
-
+
return 0;
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index a4a96826d9e0..2fe1d690eb13 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -682,34 +682,7 @@ static void pci_read_irq(struct pci_dev *dev)
dev->irq = irq;
}
-static void change_legacy_io_resource(struct pci_dev * dev, unsigned index,
- unsigned start, unsigned end)
-{
- unsigned base = start & PCI_BASE_ADDRESS_IO_MASK;
- unsigned len = (end | ~PCI_BASE_ADDRESS_IO_MASK) - base + 1;
-
- /*
- * Some X versions get confused when the BARs reported through
- * /sys or /proc differ from those seen in config space, thus
- * try to update the config space values, too.
- */
- if (!(pci_resource_flags(dev, index) & IORESOURCE_IO))
- printk(KERN_WARNING "%s: cannot adjust BAR%u (not I/O)\n",
- pci_name(dev), index);
- else if (pci_resource_len(dev, index) != len)
- printk(KERN_WARNING "%s: cannot adjust BAR%u (size %04X)\n",
- pci_name(dev), index, (unsigned)pci_resource_len(dev, index));
- else {
- printk(KERN_INFO "%s: trying to change BAR%u from %04X to %04X\n",
- pci_name(dev), index,
- (unsigned)pci_resource_start(dev, index), base);
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + index * 4, base);
- }
- pci_resource_start(dev, index) = start;
- pci_resource_end(dev, index) = end;
- pci_resource_flags(dev, index) =
- IORESOURCE_IO | IORESOURCE_PCI_FIXED | PCI_BASE_ADDRESS_SPACE_IO;
-}
+#define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
/**
* pci_setup_device - fill in class and map information of a device
@@ -762,12 +735,20 @@ static int pci_setup_device(struct pci_dev * dev)
u8 progif;
pci_read_config_byte(dev, PCI_CLASS_PROG, &progif);
if ((progif & 1) == 0) {
- change_legacy_io_resource(dev, 0, 0x1F0, 0x1F7);
- change_legacy_io_resource(dev, 1, 0x3F6, 0x3F6);
+ dev->resource[0].start = 0x1F0;
+ dev->resource[0].end = 0x1F7;
+ dev->resource[0].flags = LEGACY_IO_RESOURCE;
+ dev->resource[1].start = 0x3F6;
+ dev->resource[1].end = 0x3F6;
+ dev->resource[1].flags = LEGACY_IO_RESOURCE;
}
if ((progif & 4) == 0) {
- change_legacy_io_resource(dev, 2, 0x170, 0x177);
- change_legacy_io_resource(dev, 3, 0x376, 0x376);
+ dev->resource[2].start = 0x170;
+ dev->resource[2].end = 0x177;
+ dev->resource[2].flags = LEGACY_IO_RESOURCE;
+ dev->resource[3].start = 0x376;
+ dev->resource[3].end = 0x376;
+ dev->resource[3].flags = LEGACY_IO_RESOURCE;
}
}
break;
diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c
index 91c047a7e635..dd6384b1efce 100644
--- a/drivers/pnp/card.c
+++ b/drivers/pnp/card.c
@@ -311,7 +311,6 @@ done:
return NULL;
found:
- down_write(&dev->dev.bus->subsys.rwsem);
dev->card_link = clink;
dev->dev.driver = &drv->link.driver;
if (pnp_bus_type.probe(&dev->dev))
@@ -319,14 +318,11 @@ found:
if (device_bind_driver(&dev->dev))
goto err_out;
- up_write(&dev->dev.bus->subsys.rwsem);
-
return dev;
err_out:
dev->dev.driver = NULL;
dev->card_link = NULL;
- up_write(&dev->dev.bus->subsys.rwsem);
return NULL;
}
@@ -340,11 +336,9 @@ void pnp_release_card_device(struct pnp_dev * dev)
struct pnp_card_driver * drv = dev->card_link->driver;
if (!drv)
return;
- down_write(&dev->dev.bus->subsys.rwsem);
drv->link.remove = &card_remove;
device_release_driver(&dev->dev);
drv->link.remove = &card_remove_first;
- up_write(&dev->dev.bus->subsys.rwsem);
}
/*
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index eb5dc62f0d9c..e71929db8b06 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -398,6 +398,9 @@ dasd_change_state(struct dasd_device *device)
if (device->state == device->target)
wake_up(&dasd_init_waitq);
+
+ /* let user-space know that the device status changed */
+ kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
}
/*
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index ed70852cc915..6a89cefe99bb 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -19,6 +19,7 @@
#include <asm/debug.h>
#include <asm/uaccess.h>
+#include <asm/ipl.h>
/* This is ugly... */
#define PRINTK_HEADER "dasd_devmap:"
@@ -133,6 +134,8 @@ dasd_call_setup(char *str)
__setup ("dasd=", dasd_call_setup);
#endif /* #ifndef MODULE */
+#define DASD_IPLDEV "ipldev"
+
/*
* Read a device busid/devno from a string.
*/
@@ -141,6 +144,20 @@ dasd_busid(char **str, int *id0, int *id1, int *devno)
{
int val, old_style;
+ /* Interpret ipldev busid */
+ if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) {
+ if (ipl_info.type != IPL_TYPE_CCW) {
+ MESSAGE(KERN_ERR, "%s", "ipl device is not a ccw "
+ "device");
+ return -EINVAL;
+ }
+ *id0 = 0;
+ *id1 = ipl_info.data.ccw.dev_id.ssid;
+ *devno = ipl_info.data.ccw.dev_id.devno;
+ *str += strlen(DASD_IPLDEV);
+
+ return 0;
+ }
/* check for leading '0x' */
old_style = 0;
if ((*str)[0] == '0' && (*str)[1] == 'x') {
@@ -829,6 +846,46 @@ dasd_discipline_show(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
static ssize_t
+dasd_device_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dasd_device *device;
+ ssize_t len;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (!IS_ERR(device)) {
+ switch (device->state) {
+ case DASD_STATE_NEW:
+ len = snprintf(buf, PAGE_SIZE, "new\n");
+ break;
+ case DASD_STATE_KNOWN:
+ len = snprintf(buf, PAGE_SIZE, "detected\n");
+ break;
+ case DASD_STATE_BASIC:
+ len = snprintf(buf, PAGE_SIZE, "basic\n");
+ break;
+ case DASD_STATE_UNFMT:
+ len = snprintf(buf, PAGE_SIZE, "unformatted\n");
+ break;
+ case DASD_STATE_READY:
+ len = snprintf(buf, PAGE_SIZE, "ready\n");
+ break;
+ case DASD_STATE_ONLINE:
+ len = snprintf(buf, PAGE_SIZE, "online\n");
+ break;
+ default:
+ len = snprintf(buf, PAGE_SIZE, "no stat\n");
+ break;
+ }
+ dasd_put_device(device);
+ } else
+ len = snprintf(buf, PAGE_SIZE, "unknown\n");
+ return len;
+}
+
+static DEVICE_ATTR(status, 0444, dasd_device_status_show, NULL);
+
+static ssize_t
dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dasd_devmap *devmap;
@@ -939,6 +996,7 @@ static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
+ &dev_attr_status.attr,
&dev_attr_alias.attr,
&dev_attr_vendor.attr,
&dev_attr_uid.attr,
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 293e667b50f2..c210784bdf46 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -3,7 +3,7 @@
#
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
- sclp_info.o
+ sclp_info.o sclp_config.o sclp_chp.o
obj-$(CONFIG_TN3270) += raw3270.o
obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
@@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
obj-$(CONFIG_MONREADER) += monreader.o
obj-$(CONFIG_MONWRITER) += monwriter.o
+
+zcore_mod-objs := sclp_sdias.o zcore.o
+obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 9a328f14a641..6000bdee4082 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -813,12 +813,6 @@ con3215_unblank(void)
spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
}
-static int __init
-con3215_consetup(struct console *co, char *options)
-{
- return 0;
-}
-
/*
* The console structure for the 3215 console
*/
@@ -827,7 +821,6 @@ static struct console con3215 = {
.write = con3215_write,
.device = con3215_device,
.unblank = con3215_unblank,
- .setup = con3215_consetup,
.flags = CON_PRINTBUFFER,
};
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 8e7f2d7633d6..fd3479119eb4 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -555,12 +555,6 @@ con3270_unblank(void)
spin_unlock_irqrestore(&cp->view.lock, flags);
}
-static int __init
-con3270_consetup(struct console *co, char *options)
-{
- return 0;
-}
-
/*
* The console structure for the 3270 console
*/
@@ -569,7 +563,6 @@ static struct console con3270 = {
.write = con3270_write,
.device = con3270_device,
.unblank = con3270_unblank,
- .setup = con3270_consetup,
.flags = CON_PRINTBUFFER,
};
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index f171de3b0b11..fa62e6944057 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -15,6 +15,7 @@
#include <linux/timer.h>
#include <linux/reboot.h>
#include <linux/jiffies.h>
+#include <linux/init.h>
#include <asm/types.h>
#include <asm/s390_ext.h>
@@ -510,7 +511,7 @@ sclp_state_change_cb(struct evbuf_header *evbuf)
}
static struct sclp_register sclp_state_change_event = {
- .receive_mask = EvTyp_StateChange_Mask,
+ .receive_mask = EVTYP_STATECHANGE_MASK,
.receiver_fn = sclp_state_change_cb
};
@@ -930,3 +931,10 @@ sclp_init(void)
sclp_init_mask(1);
return 0;
}
+
+static __init int sclp_initcall(void)
+{
+ return sclp_init();
+}
+
+arch_initcall(sclp_initcall);
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 7d29ab45a6ed..87ac4a3ad49d 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -19,33 +19,37 @@
#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
#define MAX_CONSOLE_PAGES 4
-#define EvTyp_OpCmd 0x01
-#define EvTyp_Msg 0x02
-#define EvTyp_StateChange 0x08
-#define EvTyp_PMsgCmd 0x09
-#define EvTyp_CntlProgOpCmd 0x20
-#define EvTyp_CntlProgIdent 0x0B
-#define EvTyp_SigQuiesce 0x1D
-#define EvTyp_VT220Msg 0x1A
-
-#define EvTyp_OpCmd_Mask 0x80000000
-#define EvTyp_Msg_Mask 0x40000000
-#define EvTyp_StateChange_Mask 0x01000000
-#define EvTyp_PMsgCmd_Mask 0x00800000
-#define EvTyp_CtlProgOpCmd_Mask 0x00000001
-#define EvTyp_CtlProgIdent_Mask 0x00200000
-#define EvTyp_SigQuiesce_Mask 0x00000008
-#define EvTyp_VT220Msg_Mask 0x00000040
-
-#define GnrlMsgFlgs_DOM 0x8000
-#define GnrlMsgFlgs_SndAlrm 0x4000
-#define GnrlMsgFlgs_HoldMsg 0x2000
-
-#define LnTpFlgs_CntlText 0x8000
-#define LnTpFlgs_LabelText 0x4000
-#define LnTpFlgs_DataText 0x2000
-#define LnTpFlgs_EndText 0x1000
-#define LnTpFlgs_PromptText 0x0800
+#define EVTYP_OPCMD 0x01
+#define EVTYP_MSG 0x02
+#define EVTYP_STATECHANGE 0x08
+#define EVTYP_PMSGCMD 0x09
+#define EVTYP_CNTLPROGOPCMD 0x20
+#define EVTYP_CNTLPROGIDENT 0x0B
+#define EVTYP_SIGQUIESCE 0x1D
+#define EVTYP_VT220MSG 0x1A
+#define EVTYP_CONFMGMDATA 0x04
+#define EVTYP_SDIAS 0x1C
+
+#define EVTYP_OPCMD_MASK 0x80000000
+#define EVTYP_MSG_MASK 0x40000000
+#define EVTYP_STATECHANGE_MASK 0x01000000
+#define EVTYP_PMSGCMD_MASK 0x00800000
+#define EVTYP_CTLPROGOPCMD_MASK 0x00000001
+#define EVTYP_CTLPROGIDENT_MASK 0x00200000
+#define EVTYP_SIGQUIESCE_MASK 0x00000008
+#define EVTYP_VT220MSG_MASK 0x00000040
+#define EVTYP_CONFMGMDATA_MASK 0x10000000
+#define EVTYP_SDIAS_MASK 0x00000010
+
+#define GNRLMSGFLGS_DOM 0x8000
+#define GNRLMSGFLGS_SNDALRM 0x4000
+#define GNRLMSGFLGS_HOLDMSG 0x2000
+
+#define LNTPFLGS_CNTLTEXT 0x8000
+#define LNTPFLGS_LABELTEXT 0x4000
+#define LNTPFLGS_DATATEXT 0x2000
+#define LNTPFLGS_ENDTEXT 0x1000
+#define LNTPFLGS_PROMPTTEXT 0x0800
typedef unsigned int sclp_cmdw_t;
@@ -56,15 +60,15 @@ typedef unsigned int sclp_cmdw_t;
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
#define GDS_ID_MDSMU 0x1310
-#define GDS_ID_MDSRouteInfo 0x1311
-#define GDS_ID_AgUnWrkCorr 0x1549
-#define GDS_ID_SNACondReport 0x1532
+#define GDS_ID_MDSROUTEINFO 0x1311
+#define GDS_ID_AGUNWRKCORR 0x1549
+#define GDS_ID_SNACONDREPORT 0x1532
#define GDS_ID_CPMSU 0x1212
-#define GDS_ID_RoutTargInstr 0x154D
-#define GDS_ID_OpReq 0x8070
-#define GDS_ID_TextCmd 0x1320
+#define GDS_ID_ROUTTARGINSTR 0x154D
+#define GDS_ID_OPREQ 0x8070
+#define GDS_ID_TEXTCMD 0x1320
-#define GDS_KEY_SelfDefTextMsg 0x31
+#define GDS_KEY_SELFDEFTEXTMSG 0x31
typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */
diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c
new file mode 100644
index 000000000000..a66b914519b5
--- /dev/null
+++ b/drivers/s390/char/sclp_chp.c
@@ -0,0 +1,196 @@
+/*
+ * drivers/s390/char/sclp_chp.c
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/gfp.h>
+#include <linux/errno.h>
+#include <linux/completion.h>
+#include <asm/sclp.h>
+#include <asm/chpid.h>
+
+#include "sclp.h"
+
+#define TAG "sclp_chp: "
+
+#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001
+#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001
+#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001
+
+static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid)
+{
+ return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8;
+}
+
+static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid)
+{
+ return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8;
+}
+
+static void chp_callback(struct sclp_req *req, void *data)
+{
+ struct completion *completion = data;
+
+ complete(completion);
+}
+
+struct chp_cfg_sccb {
+ struct sccb_header header;
+ u8 ccm;
+ u8 reserved[6];
+ u8 cssid;
+} __attribute__((packed));
+
+struct chp_cfg_data {
+ struct chp_cfg_sccb sccb;
+ struct sclp_req req;
+ struct completion completion;
+} __attribute__((packed));
+
+static int do_configure(sclp_cmdw_t cmd)
+{
+ struct chp_cfg_data *data;
+ int rc;
+
+ /* Prepare sccb. */
+ data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+ data->sccb.header.length = sizeof(struct chp_cfg_sccb);
+ data->req.command = cmd;
+ data->req.sccb = &(data->sccb);
+ data->req.status = SCLP_REQ_FILLED;
+ data->req.callback = chp_callback;
+ data->req.callback_data = &(data->completion);
+ init_completion(&data->completion);
+
+ /* Perform sclp request. */
+ rc = sclp_add_request(&(data->req));
+ if (rc)
+ goto out;
+ wait_for_completion(&data->completion);
+
+ /* Check response .*/
+ if (data->req.status != SCLP_REQ_DONE) {
+ printk(KERN_WARNING TAG "configure channel-path request failed "
+ "(status=0x%02x)\n", data->req.status);
+ rc = -EIO;
+ goto out;
+ }
+ switch (data->sccb.header.response_code) {
+ case 0x0020:
+ case 0x0120:
+ case 0x0440:
+ case 0x0450:
+ break;
+ default:
+ printk(KERN_WARNING TAG "configure channel-path failed "
+ "(cmd=0x%08x, response=0x%04x)\n", cmd,
+ data->sccb.header.response_code);
+ rc = -EIO;
+ break;
+ }
+out:
+ free_page((unsigned long) data);
+
+ return rc;
+}
+
+/**
+ * sclp_chp_configure - perform configure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform configure channel-path command sclp command for specified chpid.
+ * Return 0 after command successfully finished, non-zero otherwise.
+ */
+int sclp_chp_configure(struct chp_id chpid)
+{
+ return do_configure(get_configure_cmdw(chpid));
+}
+
+/**
+ * sclp_chp_deconfigure - perform deconfigure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform deconfigure channel-path command sclp command for specified chpid
+ * and wait for completion. On success return 0. Return non-zero otherwise.
+ */
+int sclp_chp_deconfigure(struct chp_id chpid)
+{
+ return do_configure(get_deconfigure_cmdw(chpid));
+}
+
+struct chp_info_sccb {
+ struct sccb_header header;
+ u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
+ u8 standby[SCLP_CHP_INFO_MASK_SIZE];
+ u8 configured[SCLP_CHP_INFO_MASK_SIZE];
+ u8 ccm;
+ u8 reserved[6];
+ u8 cssid;
+} __attribute__((packed));
+
+struct chp_info_data {
+ struct chp_info_sccb sccb;
+ struct sclp_req req;
+ struct completion completion;
+} __attribute__((packed));
+
+/**
+ * sclp_chp_read_info - perform read channel-path information sclp command
+ * @info: resulting channel-path information data
+ *
+ * Perform read channel-path information sclp command and wait for completion.
+ * On success, store channel-path information in @info and return 0. Return
+ * non-zero otherwise.
+ */
+int sclp_chp_read_info(struct sclp_chp_info *info)
+{
+ struct chp_info_data *data;
+ int rc;
+
+ /* Prepare sccb. */
+ data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+ data->sccb.header.length = sizeof(struct chp_info_sccb);
+ data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION;
+ data->req.sccb = &(data->sccb);
+ data->req.status = SCLP_REQ_FILLED;
+ data->req.callback = chp_callback;
+ data->req.callback_data = &(data->completion);
+ init_completion(&data->completion);
+
+ /* Perform sclp request. */
+ rc = sclp_add_request(&(data->req));
+ if (rc)
+ goto out;
+ wait_for_completion(&data->completion);
+
+ /* Check response .*/
+ if (data->req.status != SCLP_REQ_DONE) {
+ printk(KERN_WARNING TAG "read channel-path info request failed "
+ "(status=0x%02x)\n", data->req.status);
+ rc = -EIO;
+ goto out;
+ }
+ if (data->sccb.header.response_code != 0x0010) {
+ printk(KERN_WARNING TAG "read channel-path info failed "
+ "(response=0x%04x)\n", data->sccb.header.response_code);
+ rc = -EIO;
+ goto out;
+ }
+ memcpy(info->recognized, data->sccb.recognized,
+ SCLP_CHP_INFO_MASK_SIZE);
+ memcpy(info->standby, data->sccb.standby,
+ SCLP_CHP_INFO_MASK_SIZE);
+ memcpy(info->configured, data->sccb.configured,
+ SCLP_CHP_INFO_MASK_SIZE);
+out:
+ free_page((unsigned long) data);
+
+ return rc;
+}
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
new file mode 100644
index 000000000000..5322e5e54a98
--- /dev/null
+++ b/drivers/s390/char/sclp_config.c
@@ -0,0 +1,75 @@
+/*
+ * drivers/s390/char/sclp_config.c
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/sysdev.h>
+#include <linux/workqueue.h>
+#include "sclp.h"
+
+#define TAG "sclp_config: "
+
+struct conf_mgm_data {
+ u8 reserved;
+ u8 ev_qualifier;
+} __attribute__((packed));
+
+#define EV_QUAL_CAP_CHANGE 3
+
+static struct work_struct sclp_cpu_capability_work;
+
+static void sclp_cpu_capability_notify(struct work_struct *work)
+{
+ int cpu;
+ struct sys_device *sysdev;
+
+ printk(KERN_WARNING TAG "cpu capability changed.\n");
+ lock_cpu_hotplug();
+ for_each_online_cpu(cpu) {
+ sysdev = get_cpu_sysdev(cpu);
+ kobject_uevent(&sysdev->kobj, KOBJ_CHANGE);
+ }
+ unlock_cpu_hotplug();
+}
+
+static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
+{
+ struct conf_mgm_data *cdata;
+
+ cdata = (struct conf_mgm_data *)(evbuf + 1);
+ if (cdata->ev_qualifier == EV_QUAL_CAP_CHANGE)
+ schedule_work(&sclp_cpu_capability_work);
+}
+
+static struct sclp_register sclp_conf_register =
+{
+ .receive_mask = EVTYP_CONFMGMDATA_MASK,
+ .receiver_fn = sclp_conf_receiver_fn,
+};
+
+static int __init sclp_conf_init(void)
+{
+ int rc;
+
+ INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
+
+ rc = sclp_register(&sclp_conf_register);
+ if (rc) {
+ printk(KERN_ERR TAG "failed to register (%d).\n", rc);
+ return rc;
+ }
+
+ if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK)) {
+ printk(KERN_WARNING TAG "no configuration management.\n");
+ sclp_unregister(&sclp_conf_register);
+ rc = -ENOSYS;
+ }
+ return rc;
+}
+
+__initcall(sclp_conf_init);
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c
index 65aa2c85737f..29fe2a5ec2fe 100644
--- a/drivers/s390/char/sclp_cpi.c
+++ b/drivers/s390/char/sclp_cpi.c
@@ -46,7 +46,7 @@ struct cpi_sccb {
/* Event type structure for write message and write priority message */
static struct sclp_register sclp_cpi_event =
{
- .send_mask = EvTyp_CtlProgIdent_Mask
+ .send_mask = EVTYP_CTLPROGIDENT_MASK
};
MODULE_LICENSE("GPL");
@@ -201,7 +201,7 @@ cpi_module_init(void)
"console.\n");
return -EINVAL;
}
- if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) {
+ if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) {
printk(KERN_WARNING "cpi: no control program identification "
"support\n");
sclp_unregister(&sclp_cpi_event);
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index baa8fe669ed2..45ff25e787cb 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -43,7 +43,7 @@ sclp_quiesce_handler(struct evbuf_header *evbuf)
}
static struct sclp_register sclp_quiesce_event = {
- .receive_mask = EvTyp_SigQuiesce_Mask,
+ .receive_mask = EVTYP_SIGQUIESCE_MASK,
.receiver_fn = sclp_quiesce_handler
};
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 2486783ea58e..bbd5b8b66f42 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -30,7 +30,7 @@
/* Event type structure for write message and write priority message */
static struct sclp_register sclp_rw_event = {
- .send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask
+ .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK
};
/*
@@ -64,7 +64,7 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
memset(sccb, 0, sizeof(struct write_sccb));
sccb->header.length = sizeof(struct write_sccb);
sccb->msg_buf.header.length = sizeof(struct msg_buf);
- sccb->msg_buf.header.type = EvTyp_Msg;
+ sccb->msg_buf.header.type = EVTYP_MSG;
sccb->msg_buf.mdb.header.length = sizeof(struct mdb);
sccb->msg_buf.mdb.header.type = 1;
sccb->msg_buf.mdb.header.tag = 0xD4C4C240; /* ebcdic "MDB " */
@@ -114,7 +114,7 @@ sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
memset(mto, 0, sizeof(struct mto));
mto->length = sizeof(struct mto);
mto->type = 4; /* message text object */
- mto->line_type_flags = LnTpFlgs_EndText; /* end text */
+ mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */
/* set pointer to first byte after struct mto. */
buffer->current_line = (char *) (mto + 1);
@@ -215,7 +215,7 @@ sclp_write(struct sclp_buffer *buffer, const unsigned char *msg, int count)
case '\a': /* bell, one for several times */
/* set SCLP sound alarm bit in General Object */
buffer->sccb->msg_buf.mdb.go.general_msg_flags |=
- GnrlMsgFlgs_SndAlrm;
+ GNRLMSGFLGS_SNDALRM;
break;
case '\t': /* horizontal tabulator */
/* check if new mto needs to be created */
@@ -452,12 +452,12 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
return -EIO;
sccb = buffer->sccb;
- if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask)
+ if (sclp_rw_event.sclp_send_mask & EVTYP_MSG_MASK)
/* Use normal write message */
- sccb->msg_buf.header.type = EvTyp_Msg;
- else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask)
+ sccb->msg_buf.header.type = EVTYP_MSG;
+ else if (sclp_rw_event.sclp_send_mask & EVTYP_PMSGCMD_MASK)
/* Use write priority message */
- sccb->msg_buf.header.type = EvTyp_PMsgCmd;
+ sccb->msg_buf.header.type = EVTYP_PMSGCMD;
else
return -ENOSYS;
buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
new file mode 100644
index 000000000000..52283daddaef
--- /dev/null
+++ b/drivers/s390/char/sclp_sdias.c
@@ -0,0 +1,255 @@
+/*
+ * Sclp "store data in absolut storage"
+ *
+ * Copyright IBM Corp. 2003,2007
+ * Author(s): Michael Holzheu
+ */
+
+#include <linux/sched.h>
+#include <asm/sclp.h>
+#include <asm/debug.h>
+#include <asm/ipl.h>
+#include "sclp.h"
+#include "sclp_rw.h"
+
+#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
+#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
+
+#define SDIAS_RETRIES 300
+#define SDIAS_SLEEP_TICKS 50
+
+#define EQ_STORE_DATA 0x0
+#define EQ_SIZE 0x1
+#define DI_FCP_DUMP 0x0
+#define ASA_SIZE_32 0x0
+#define ASA_SIZE_64 0x1
+#define EVSTATE_ALL_STORED 0x0
+#define EVSTATE_NO_DATA 0x3
+#define EVSTATE_PART_STORED 0x10
+
+static struct debug_info *sdias_dbf;
+
+static struct sclp_register sclp_sdias_register = {
+ .send_mask = EVTYP_SDIAS_MASK,
+};
+
+struct sdias_evbuf {
+ struct evbuf_header hdr;
+ u8 event_qual;
+ u8 data_id;
+ u64 reserved2;
+ u32 event_id;
+ u16 reserved3;
+ u8 asa_size;
+ u8 event_status;
+ u32 reserved4;
+ u32 blk_cnt;
+ u64 asa;
+ u32 reserved5;
+ u32 fbn;
+ u32 reserved6;
+ u32 lbn;
+ u16 reserved7;
+ u16 dbs;
+} __attribute__((packed));
+
+struct sdias_sccb {
+ struct sccb_header hdr;
+ struct sdias_evbuf evbuf;
+} __attribute__((packed));
+
+static struct sdias_sccb sccb __attribute__((aligned(4096)));
+
+static int sclp_req_done;
+static wait_queue_head_t sdias_wq;
+static DEFINE_MUTEX(sdias_mutex);
+
+static void sdias_callback(struct sclp_req *request, void *data)
+{
+ struct sdias_sccb *sccb;
+
+ sccb = (struct sdias_sccb *) request->sccb;
+ sclp_req_done = 1;
+ wake_up(&sdias_wq); /* Inform caller, that request is complete */
+ TRACE("callback done\n");
+}
+
+static int sdias_sclp_send(struct sclp_req *req)
+{
+ int retries;
+ int rc;
+
+ for (retries = SDIAS_RETRIES; retries; retries--) {
+ sclp_req_done = 0;
+ TRACE("add request\n");
+ rc = sclp_add_request(req);
+ if (rc) {
+ /* not initiated, wait some time and retry */
+ set_current_state(TASK_INTERRUPTIBLE);
+ TRACE("add request failed: rc = %i\n",rc);
+ schedule_timeout(SDIAS_SLEEP_TICKS);
+ continue;
+ }
+ /* initiated, wait for completion of service call */
+ wait_event(sdias_wq, (sclp_req_done == 1));
+ if (req->status == SCLP_REQ_FAILED) {
+ TRACE("sclp request failed\n");
+ rc = -EIO;
+ continue;
+ }
+ TRACE("request done\n");
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Get number of blocks (4K) available in the HSA
+ */
+int sclp_sdias_blk_count(void)
+{
+ struct sclp_req request;
+ int rc;
+
+ mutex_lock(&sdias_mutex);
+
+ memset(&sccb, 0, sizeof(sccb));
+ memset(&request, 0, sizeof(request));
+
+ sccb.hdr.length = sizeof(sccb);
+ sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
+ sccb.evbuf.hdr.type = EVTYP_SDIAS;
+ sccb.evbuf.event_qual = EQ_SIZE;
+ sccb.evbuf.data_id = DI_FCP_DUMP;
+ sccb.evbuf.event_id = 4712;
+ sccb.evbuf.dbs = 1;
+
+ request.sccb = &sccb;
+ request.command = SCLP_CMDW_WRITE_EVENT_DATA;
+ request.status = SCLP_REQ_FILLED;
+ request.callback = sdias_callback;
+
+ rc = sdias_sclp_send(&request);
+ if (rc) {
+ ERROR_MSG("sclp_send failed for get_nr_blocks\n");
+ goto out;
+ }
+ if (sccb.hdr.response_code != 0x0020) {
+ TRACE("send failed: %x\n", sccb.hdr.response_code);
+ rc = -EIO;
+ goto out;
+ }
+
+ switch (sccb.evbuf.event_status) {
+ case 0:
+ rc = sccb.evbuf.blk_cnt;
+ break;
+ default:
+ ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status);
+ rc = -EIO;
+ goto out;
+ }
+ TRACE("%i blocks\n", rc);
+out:
+ mutex_unlock(&sdias_mutex);
+ return rc;
+}
+
+/*
+ * Copy from HSA to absolute storage (not reentrant):
+ *
+ * @dest : Address of buffer where data should be copied
+ * @start_blk: Start Block (beginning with 1)
+ * @nr_blks : Number of 4K blocks to copy
+ *
+ * Return Value: 0 : Requested 'number' of blocks of data copied
+ * <0: ERROR - negative event status
+ */
+int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
+{
+ struct sclp_req request;
+ int rc;
+
+ mutex_lock(&sdias_mutex);
+
+ memset(&sccb, 0, sizeof(sccb));
+ memset(&request, 0, sizeof(request));
+
+ sccb.hdr.length = sizeof(sccb);
+ sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
+ sccb.evbuf.hdr.type = EVTYP_SDIAS;
+ sccb.evbuf.hdr.flags = 0;
+ sccb.evbuf.event_qual = EQ_STORE_DATA;
+ sccb.evbuf.data_id = DI_FCP_DUMP;
+ sccb.evbuf.event_id = 4712;
+#ifdef __s390x__
+ sccb.evbuf.asa_size = ASA_SIZE_64;
+#else
+ sccb.evbuf.asa_size = ASA_SIZE_32;
+#endif
+ sccb.evbuf.event_status = 0;
+ sccb.evbuf.blk_cnt = nr_blks;
+ sccb.evbuf.asa = (unsigned long)dest;
+ sccb.evbuf.fbn = start_blk;
+ sccb.evbuf.lbn = 0;
+ sccb.evbuf.dbs = 1;
+
+ request.sccb = &sccb;
+ request.command = SCLP_CMDW_WRITE_EVENT_DATA;
+ request.status = SCLP_REQ_FILLED;
+ request.callback = sdias_callback;
+
+ rc = sdias_sclp_send(&request);
+ if (rc) {
+ ERROR_MSG("sclp_send failed: %x\n", rc);
+ goto out;
+ }
+ if (sccb.hdr.response_code != 0x0020) {
+ TRACE("copy failed: %x\n", sccb.hdr.response_code);
+ rc = -EIO;
+ goto out;
+ }
+
+ switch (sccb.evbuf.event_status) {
+ case EVSTATE_ALL_STORED:
+ TRACE("all stored\n");
+ case EVSTATE_PART_STORED:
+ TRACE("part stored: %i\n", sccb.evbuf.blk_cnt);
+ break;
+ case EVSTATE_NO_DATA:
+ TRACE("no data\n");
+ default:
+ ERROR_MSG("Error from SCLP while copying hsa. "
+ "Event status = %x\n",
+ sccb.evbuf.event_status);
+ rc = -EIO;
+ }
+out:
+ mutex_unlock(&sdias_mutex);
+ return rc;
+}
+
+int __init sdias_init(void)
+{
+ int rc;
+
+ if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+ return 0;
+ sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
+ debug_register_view(sdias_dbf, &debug_sprintf_view);
+ debug_set_level(sdias_dbf, 6);
+ rc = sclp_register(&sclp_sdias_register);
+ if (rc) {
+ ERROR_MSG("sclp register failed\n");
+ return rc;
+ }
+ init_waitqueue_head(&sdias_wq);
+ TRACE("init done\n");
+ return 0;
+}
+
+void __exit sdias_exit(void)
+{
+ debug_unregister(sdias_dbf);
+ sclp_unregister(&sclp_sdias_register);
+}
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 076816b9d528..e3b3d390b4a3 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -648,7 +648,7 @@ sclp_eval_textcmd(struct gds_subvector *start,
subvec = start;
while (subvec < end) {
subvec = find_gds_subvector(subvec, end,
- GDS_KEY_SelfDefTextMsg);
+ GDS_KEY_SELFDEFTEXTMSG);
if (!subvec)
break;
sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
@@ -664,7 +664,7 @@ sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end)
vec = start;
while (vec < end) {
- vec = find_gds_vector(vec, end, GDS_ID_TextCmd);
+ vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD);
if (!vec)
break;
sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
@@ -703,7 +703,7 @@ sclp_tty_state_change(struct sclp_register *reg)
static struct sclp_register sclp_input_event =
{
- .receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask,
+ .receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK,
.state_change_fn = sclp_tty_state_change,
.receiver_fn = sclp_tty_receiver
};
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index f77dc33b5f8d..726334757bbf 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -99,8 +99,8 @@ static void sclp_vt220_emit_current(void);
/* Registration structure for our interest in SCLP event buffers */
static struct sclp_register sclp_vt220_register = {
- .send_mask = EvTyp_VT220Msg_Mask,
- .receive_mask = EvTyp_VT220Msg_Mask,
+ .send_mask = EVTYP_VT220MSG_MASK,
+ .receive_mask = EVTYP_VT220MSG_MASK,
.state_change_fn = NULL,
.receiver_fn = sclp_vt220_receiver_fn
};
@@ -202,7 +202,7 @@ sclp_vt220_callback(struct sclp_req *request, void *data)
static int
__sclp_vt220_emit(struct sclp_vt220_request *request)
{
- if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) {
+ if (!(sclp_vt220_register.sclp_send_mask & EVTYP_VT220MSG_MASK)) {
request->sclp_req.status = SCLP_REQ_FAILED;
return -EIO;
}
@@ -284,7 +284,7 @@ sclp_vt220_initialize_page(void *page)
sccb->header.length = sizeof(struct sclp_vt220_sccb);
sccb->header.function_code = SCLP_NORMAL_WRITE;
sccb->header.response_code = 0x0000;
- sccb->evbuf.type = EvTyp_VT220Msg;
+ sccb->evbuf.type = EVTYP_VT220MSG;
sccb->evbuf.length = sizeof(struct evbuf_header);
return request;
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index b87d3b019936..a5a00e9ae4d0 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -125,7 +125,7 @@ static struct vmlogrdr_priv_t sys_ser[] = {
.recording_name = "EREP",
.minor_num = 0,
.buffer_free = 1,
- .priv_lock = SPIN_LOCK_UNLOCKED,
+ .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock),
.autorecording = 1,
.autopurge = 1,
},
@@ -134,7 +134,7 @@ static struct vmlogrdr_priv_t sys_ser[] = {
.recording_name = "ACCOUNT",
.minor_num = 1,
.buffer_free = 1,
- .priv_lock = SPIN_LOCK_UNLOCKED,
+ .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock),
.autorecording = 1,
.autopurge = 1,
},
@@ -143,7 +143,7 @@ static struct vmlogrdr_priv_t sys_ser[] = {
.recording_name = "SYMPTOM",
.minor_num = 2,
.buffer_free = 1,
- .priv_lock = SPIN_LOCK_UNLOCKED,
+ .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock),
.autorecording = 1,
.autopurge = 1,
}
@@ -385,6 +385,9 @@ static int vmlogrdr_release (struct inode *inode, struct file *filp)
struct vmlogrdr_priv_t * logptr = filp->private_data;
+ iucv_path_sever(logptr->path, NULL);
+ kfree(logptr->path);
+ logptr->path = NULL;
if (logptr->autorecording) {
ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
if (ret)
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
new file mode 100644
index 000000000000..89d439316a53
--- /dev/null
+++ b/drivers/s390/char/zcore.c
@@ -0,0 +1,651 @@
+/*
+ * zcore module to export memory content and register sets for creating system
+ * dumps on SCSI disks (zfcpdump). The "zcore/mem" debugfs file shows the same
+ * dump format as s390 standalone dumps.
+ *
+ * For more information please refer to Documentation/s390/zfcpdump.txt
+ *
+ * Copyright IBM Corp. 2003,2007
+ * Author(s): Michael Holzheu
+ */
+
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/utsname.h>
+#include <linux/debugfs.h>
+#include <asm/ipl.h>
+#include <asm/sclp.h>
+#include <asm/setup.h>
+#include <asm/sigp.h>
+#include <asm/uaccess.h>
+#include <asm/debug.h>
+#include <asm/processor.h>
+#include <asm/irqflags.h>
+
+#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
+#define MSG(x...) printk( KERN_ALERT x )
+#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x )
+
+#define TO_USER 0
+#define TO_KERNEL 1
+
+enum arch_id {
+ ARCH_S390 = 0,
+ ARCH_S390X = 1,
+};
+
+/* dump system info */
+
+struct sys_info {
+ enum arch_id arch;
+ unsigned long sa_base;
+ u32 sa_size;
+ int cpu_map[NR_CPUS];
+ unsigned long mem_size;
+ union save_area lc_mask;
+};
+
+static struct sys_info sys_info;
+static struct debug_info *zcore_dbf;
+static int hsa_available;
+static struct dentry *zcore_dir;
+static struct dentry *zcore_file;
+
+/*
+ * Copy memory from HSA to kernel or user memory (not reentrant):
+ *
+ * @dest: Kernel or user buffer where memory should be copied to
+ * @src: Start address within HSA where data should be copied
+ * @count: Size of buffer, which should be copied
+ * @mode: Either TO_KERNEL or TO_USER
+ */
+static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
+{
+ int offs, blk_num;
+ static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+
+ if (count == 0)
+ return 0;
+
+ /* copy first block */
+ offs = 0;
+ if ((src % PAGE_SIZE) != 0) {
+ blk_num = src / PAGE_SIZE + 2;
+ if (sclp_sdias_copy(buf, blk_num, 1)) {
+ TRACE("sclp_sdias_copy() failed\n");
+ return -EIO;
+ }
+ offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count);
+ if (mode == TO_USER) {
+ if (copy_to_user((__force __user void*) dest,
+ buf + (src % PAGE_SIZE), offs))
+ return -EFAULT;
+ } else
+ memcpy(dest, buf + (src % PAGE_SIZE), offs);
+ }
+ if (offs == count)
+ goto out;
+
+ /* copy middle */
+ for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) {
+ blk_num = (src + offs) / PAGE_SIZE + 2;
+ if (sclp_sdias_copy(buf, blk_num, 1)) {
+ TRACE("sclp_sdias_copy() failed\n");
+ return -EIO;
+ }
+ if (mode == TO_USER) {
+ if (copy_to_user((__force __user void*) dest + offs,
+ buf, PAGE_SIZE))
+ return -EFAULT;
+ } else
+ memcpy(dest + offs, buf, PAGE_SIZE);
+ }
+ if (offs == count)
+ goto out;
+
+ /* copy last block */
+ blk_num = (src + offs) / PAGE_SIZE + 2;
+ if (sclp_sdias_copy(buf, blk_num, 1)) {
+ TRACE("sclp_sdias_copy() failed\n");
+ return -EIO;
+ }
+ if (mode == TO_USER) {
+ if (copy_to_user((__force __user void*) dest + offs, buf,
+ PAGE_SIZE))
+ return -EFAULT;
+ } else
+ memcpy(dest + offs, buf, count - offs);
+out:
+ return 0;
+}
+
+static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
+{
+ return memcpy_hsa((void __force *) dest, src, count, TO_USER);
+}
+
+static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
+{
+ return memcpy_hsa(dest, src, count, TO_KERNEL);
+}
+
+static int memcpy_real(void *dest, unsigned long src, size_t count)
+{
+ unsigned long flags;
+ int rc = -EFAULT;
+ register unsigned long _dest asm("2") = (unsigned long) dest;
+ register unsigned long _len1 asm("3") = (unsigned long) count;
+ register unsigned long _src asm("4") = src;
+ register unsigned long _len2 asm("5") = (unsigned long) count;
+
+ if (count == 0)
+ return 0;
+ flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */
+ asm volatile (
+ "0: mvcle %1,%2,0x0\n"
+ "1: jo 0b\n"
+ " lhi %0,0x0\n"
+ "2:\n"
+ EX_TABLE(1b,2b)
+ : "+d" (rc)
+ : "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2)
+ : "cc", "memory");
+ __raw_local_irq_ssm(flags);
+
+ return rc;
+}
+
+static int memcpy_real_user(__user void *dest, unsigned long src, size_t count)
+{
+ static char buf[4096];
+ int offs = 0, size;
+
+ while (offs < count) {
+ size = min(sizeof(buf), count - offs);
+ if (memcpy_real(buf, src + offs, size))
+ return -EFAULT;
+ if (copy_to_user(dest + offs, buf, size))
+ return -EFAULT;
+ offs += size;
+ }
+ return 0;
+}
+
+#ifdef __s390x__
+/*
+ * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info
+ */
+static void __init s390x_to_s390_regs(union save_area *out, union save_area *in,
+ int cpu)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 0x00000000ffffffff;
+ out->s390.acc_regs[i] = in->s390x.acc_regs[i];
+ out->s390.ctrl_regs[i] =
+ in->s390x.ctrl_regs[i] & 0x00000000ffffffff;
+ }
+ /* locore for 31 bit has only space for fpregs 0,2,4,6 */
+ out->s390.fp_regs[0] = in->s390x.fp_regs[0];
+ out->s390.fp_regs[1] = in->s390x.fp_regs[2];
+ out->s390.fp_regs[2] = in->s390x.fp_regs[4];
+ out->s390.fp_regs[3] = in->s390x.fp_regs[6];
+ memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4);
+ out->s390.psw[1] |= 0x8; /* set bit 12 */
+ memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4);
+ out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */
+ out->s390.pref_reg = in->s390x.pref_reg;
+ out->s390.timer = in->s390x.timer;
+ out->s390.clk_cmp = in->s390x.clk_cmp;
+}
+
+static void __init s390x_to_s390_save_areas(void)
+{
+ int i = 1;
+ static union save_area tmp;
+
+ while (zfcpdump_save_areas[i]) {
+ s390x_to_s390_regs(&tmp, zfcpdump_save_areas[i], i);
+ memcpy(zfcpdump_save_areas[i], &tmp, sizeof(tmp));
+ i++;
+ }
+}
+
+#endif /* __s390x__ */
+
+static int __init init_cpu_info(enum arch_id arch)
+{
+ union save_area *sa;
+
+ /* get info for boot cpu from lowcore, stored in the HSA */
+
+ sa = kmalloc(sizeof(*sa), GFP_KERNEL);
+ if (!sa) {
+ ERROR_MSG("kmalloc failed: %s: %i\n",__FUNCTION__, __LINE__);
+ return -ENOMEM;
+ }
+ if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) {
+ ERROR_MSG("could not copy from HSA\n");
+ kfree(sa);
+ return -EIO;
+ }
+ zfcpdump_save_areas[0] = sa;
+
+#ifdef __s390x__
+ /* convert s390x regs to s390, if we are dumping an s390 Linux */
+
+ if (arch == ARCH_S390)
+ s390x_to_s390_save_areas();
+#endif
+
+ return 0;
+}
+
+static DEFINE_MUTEX(zcore_mutex);
+
+#define DUMP_VERSION 0x3
+#define DUMP_MAGIC 0xa8190173618f23fdULL
+#define DUMP_ARCH_S390X 2
+#define DUMP_ARCH_S390 1
+#define HEADER_SIZE 4096
+
+/* dump header dumped according to s390 crash dump format */
+
+struct zcore_header {
+ u64 magic;
+ u32 version;
+ u32 header_size;
+ u32 dump_level;
+ u32 page_size;
+ u64 mem_size;
+ u64 mem_start;
+ u64 mem_end;
+ u32 num_pages;
+ u32 pad1;
+ u64 tod;
+ cpuid_t cpu_id;
+ u32 arch_id;
+ u32 build_arch;
+ char pad2[4016];
+} __attribute__((packed,__aligned__(16)));
+
+static struct zcore_header zcore_header = {
+ .magic = DUMP_MAGIC,
+ .version = DUMP_VERSION,
+ .header_size = 4096,
+ .dump_level = 0,
+ .page_size = PAGE_SIZE,
+ .mem_start = 0,
+#ifdef __s390x__
+ .build_arch = DUMP_ARCH_S390X,
+#else
+ .build_arch = DUMP_ARCH_S390,
+#endif
+};
+
+/*
+ * Copy lowcore info to buffer. Use map in order to copy only register parts.
+ *
+ * @buf: User buffer
+ * @sa: Pointer to save area
+ * @sa_off: Offset in save area to copy
+ * @len: Number of bytes to copy
+ */
+static int copy_lc(void __user *buf, void *sa, int sa_off, int len)
+{
+ int i;
+ char *lc_mask = (char*)&sys_info.lc_mask;
+
+ for (i = 0; i < len; i++) {
+ if (!lc_mask[i + sa_off])
+ continue;
+ if (copy_to_user(buf + i, sa + sa_off + i, 1))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/*
+ * Copy lowcores info to memory, if necessary
+ *
+ * @buf: User buffer
+ * @addr: Start address of buffer in dump memory
+ * @count: Size of buffer
+ */
+static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)
+{
+ unsigned long end;
+ int i = 0;
+
+ if (count == 0)
+ return 0;
+
+ end = start + count;
+ while (zfcpdump_save_areas[i]) {
+ unsigned long cp_start, cp_end; /* copy range */
+ unsigned long sa_start, sa_end; /* save area range */
+ unsigned long prefix;
+ unsigned long sa_off, len, buf_off;
+
+ if (sys_info.arch == ARCH_S390)
+ prefix = zfcpdump_save_areas[i]->s390.pref_reg;
+ else
+ prefix = zfcpdump_save_areas[i]->s390x.pref_reg;
+
+ sa_start = prefix + sys_info.sa_base;
+ sa_end = prefix + sys_info.sa_base + sys_info.sa_size;
+
+ if ((end < sa_start) || (start > sa_end))
+ goto next;
+ cp_start = max(start, sa_start);
+ cp_end = min(end, sa_end);
+
+ buf_off = cp_start - start;
+ sa_off = cp_start - sa_start;
+ len = cp_end - cp_start;
+
+ TRACE("copy_lc for: %lx\n", start);
+ if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len))
+ return -EFAULT;
+next:
+ i++;
+ }
+ return 0;
+}
+
+/*
+ * Read routine for zcore character device
+ * First 4K are dump header
+ * Next 32MB are HSA Memory
+ * Rest is read from absolute Memory
+ */
+static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long mem_start; /* Start address in memory */
+ size_t mem_offs; /* Offset in dump memory */
+ size_t hdr_count; /* Size of header part of output buffer */
+ size_t size;
+ int rc;
+
+ mutex_lock(&zcore_mutex);
+
+ if (*ppos > (sys_info.mem_size + HEADER_SIZE)) {
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos));
+
+ /* Copy dump header */
+ if (*ppos < HEADER_SIZE) {
+ size = min(count, (size_t) (HEADER_SIZE - *ppos));
+ if (copy_to_user(buf, &zcore_header + *ppos, size)) {
+ rc = -EFAULT;
+ goto fail;
+ }
+ hdr_count = size;
+ mem_start = 0;
+ } else {
+ hdr_count = 0;
+ mem_start = *ppos - HEADER_SIZE;
+ }
+
+ mem_offs = 0;
+
+ /* Copy from HSA data */
+ if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) {
+ size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE
+ - mem_start));
+ rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
+ if (rc)
+ goto fail;
+
+ mem_offs += size;
+ }
+
+ /* Copy from real mem */
+ size = count - mem_offs - hdr_count;
+ rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs,
+ size);
+ if (rc)
+ goto fail;
+
+ /*
+ * Since s390 dump analysis tools like lcrash or crash
+ * expect register sets in the prefix pages of the cpus,
+ * we copy them into the read buffer, if necessary.
+ * buf + hdr_count: Start of memory part of output buffer
+ * mem_start: Start memory address to copy from
+ * count - hdr_count: Size of memory area to copy
+ */
+ if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) {
+ rc = -EFAULT;
+ goto fail;
+ }
+ *ppos += count;
+fail:
+ mutex_unlock(&zcore_mutex);
+ return (rc < 0) ? rc : count;
+}
+
+static int zcore_open(struct inode *inode, struct file *filp)
+{
+ if (!hsa_available)
+ return -ENODATA;
+ else
+ return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+}
+
+static int zcore_release(struct inode *inode, struct file *filep)
+{
+ diag308(DIAG308_REL_HSA, NULL);
+ hsa_available = 0;
+ return 0;
+}
+
+static loff_t zcore_lseek(struct file *file, loff_t offset, int orig)
+{
+ loff_t rc;
+
+ mutex_lock(&zcore_mutex);
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ rc = file->f_pos;
+ break;
+ case 1:
+ file->f_pos += offset;
+ rc = file->f_pos;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&zcore_mutex);
+ return rc;
+}
+
+static struct file_operations zcore_fops = {
+ .owner = THIS_MODULE,
+ .llseek = zcore_lseek,
+ .read = zcore_read,
+ .open = zcore_open,
+ .release = zcore_release,
+};
+
+
+static void __init set_s390_lc_mask(union save_area *map)
+{
+ memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save));
+ memset(&map->s390.timer, 0xff, sizeof(map->s390.timer));
+ memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp));
+ memset(&map->s390.psw, 0xff, sizeof(map->s390.psw));
+ memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg));
+ memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs));
+ memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs));
+ memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs));
+ memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs));
+}
+
+static void __init set_s390x_lc_mask(union save_area *map)
+{
+ memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs));
+ memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs));
+ memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw));
+ memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg));
+ memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg));
+ memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg));
+ memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer));
+ memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp));
+ memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs));
+ memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs));
+}
+
+/*
+ * Initialize dump globals for a given architecture
+ */
+static int __init sys_info_init(enum arch_id arch)
+{
+ switch (arch) {
+ case ARCH_S390X:
+ MSG("DETECTED 'S390X (64 bit) OS'\n");
+ sys_info.sa_base = SAVE_AREA_BASE_S390X;
+ sys_info.sa_size = sizeof(struct save_area_s390x);
+ set_s390x_lc_mask(&sys_info.lc_mask);
+ break;
+ case ARCH_S390:
+ MSG("DETECTED 'S390 (32 bit) OS'\n");
+ sys_info.sa_base = SAVE_AREA_BASE_S390;
+ sys_info.sa_size = sizeof(struct save_area_s390);
+ set_s390_lc_mask(&sys_info.lc_mask);
+ break;
+ default:
+ ERROR_MSG("unknown architecture 0x%x.\n",arch);
+ return -EINVAL;
+ }
+ sys_info.arch = arch;
+ if (init_cpu_info(arch)) {
+ ERROR_MSG("get cpu info failed\n");
+ return -ENOMEM;
+ }
+ sys_info.mem_size = real_memory_size;
+
+ return 0;
+}
+
+static int __init check_sdias(void)
+{
+ int rc, act_hsa_size;
+
+ rc = sclp_sdias_blk_count();
+ if (rc < 0) {
+ ERROR_MSG("Could not determine HSA size\n");
+ return rc;
+ }
+ act_hsa_size = (rc - 1) * PAGE_SIZE;
+ if (act_hsa_size < ZFCPDUMP_HSA_SIZE) {
+ ERROR_MSG("HSA size too small: %i\n", act_hsa_size);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void __init zcore_header_init(int arch, struct zcore_header *hdr)
+{
+ if (arch == ARCH_S390X)
+ hdr->arch_id = DUMP_ARCH_S390X;
+ else
+ hdr->arch_id = DUMP_ARCH_S390;
+ hdr->mem_size = sys_info.mem_size;
+ hdr->mem_end = sys_info.mem_size;
+ hdr->num_pages = sys_info.mem_size / PAGE_SIZE;
+ hdr->tod = get_clock();
+ get_cpu_id(&hdr->cpu_id);
+}
+
+extern int sdias_init(void);
+
+static int __init zcore_init(void)
+{
+ unsigned char arch;
+ int rc;
+
+ if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+ return -ENODATA;
+
+ zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long));
+ debug_register_view(zcore_dbf, &debug_sprintf_view);
+ debug_set_level(zcore_dbf, 6);
+
+ TRACE("devno: %x\n", ipl_info.data.fcp.dev_id.devno);
+ TRACE("wwpn: %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn);
+ TRACE("lun: %llx\n", (unsigned long long) ipl_info.data.fcp.lun);
+
+ rc = sdias_init();
+ if (rc)
+ goto fail;
+
+ rc = check_sdias();
+ if (rc) {
+ ERROR_MSG("Dump initialization failed\n");
+ goto fail;
+ }
+
+ rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);
+ if (rc) {
+ ERROR_MSG("sdial memcpy for arch id failed\n");
+ goto fail;
+ }
+
+#ifndef __s390x__
+ if (arch == ARCH_S390X) {
+ ERROR_MSG("32 bit dumper can't dump 64 bit system!\n");
+ rc = -EINVAL;
+ goto fail;
+ }
+#endif
+
+ rc = sys_info_init(arch);
+ if (rc) {
+ ERROR_MSG("arch init failed\n");
+ goto fail;
+ }
+
+ zcore_header_init(arch, &zcore_header);
+
+ zcore_dir = debugfs_create_dir("zcore" , NULL);
+ if (!zcore_dir) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+ zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL,
+ &zcore_fops);
+ if (!zcore_file) {
+ debugfs_remove(zcore_dir);
+ rc = -ENOMEM;
+ goto fail;
+ }
+ hsa_available = 1;
+ return 0;
+
+fail:
+ diag308(DIAG308_REL_HSA, NULL);
+ return rc;
+}
+
+extern void sdias_exit(void);
+
+static void __exit zcore_exit(void)
+{
+ debug_unregister(zcore_dbf);
+ sdias_exit();
+ diag308(DIAG308_REL_HSA, NULL);
+}
+
+MODULE_AUTHOR("Copyright IBM Corp. 2003,2007");
+MODULE_DESCRIPTION("zcore module for zfcpdump support");
+MODULE_LICENSE("GPL");
+
+subsys_initcall(zcore_init);
+module_exit(zcore_exit);
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index c490c2a1c2fc..cfaf77b320f5 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -2,7 +2,7 @@
# Makefile for the S/390 common i/o drivers
#
-obj-y += airq.o blacklist.o chsc.o cio.o css.o
+obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o
ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 5aeb68e732b0..e5ccda63e883 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -75,8 +75,10 @@ static void ccwgroup_ungroup_callback(struct device *dev)
{
struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ mutex_lock(&gdev->reg_mutex);
__ccwgroup_remove_symlinks(gdev);
device_unregister(dev);
+ mutex_unlock(&gdev->reg_mutex);
}
static ssize_t
@@ -173,7 +175,8 @@ ccwgroup_create(struct device *root,
return -ENOMEM;
atomic_set(&gdev->onoff, 0);
-
+ mutex_init(&gdev->reg_mutex);
+ mutex_lock(&gdev->reg_mutex);
for (i = 0; i < argc; i++) {
gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
@@ -183,12 +186,12 @@ ccwgroup_create(struct device *root,
|| gdev->cdev[i]->id.driver_info !=
gdev->cdev[0]->id.driver_info) {
rc = -EINVAL;
- goto free_dev;
+ goto error;
}
/* Don't allow a device to belong to more than one group. */
if (gdev->cdev[i]->dev.driver_data) {
rc = -EINVAL;
- goto free_dev;
+ goto error;
}
gdev->cdev[i]->dev.driver_data = gdev;
}
@@ -203,9 +206,8 @@ ccwgroup_create(struct device *root,
gdev->cdev[0]->dev.bus_id);
rc = device_register(&gdev->dev);
-
if (rc)
- goto free_dev;
+ goto error;
get_device(&gdev->dev);
rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
@@ -216,6 +218,7 @@ ccwgroup_create(struct device *root,
rc = __ccwgroup_create_symlinks(gdev);
if (!rc) {
+ mutex_unlock(&gdev->reg_mutex);
put_device(&gdev->dev);
return 0;
}
@@ -224,19 +227,12 @@ ccwgroup_create(struct device *root,
error:
for (i = 0; i < argc; i++)
if (gdev->cdev[i]) {
- put_device(&gdev->cdev[i]->dev);
- gdev->cdev[i]->dev.driver_data = NULL;
- }
- put_device(&gdev->dev);
- return rc;
-free_dev:
- for (i = 0; i < argc; i++)
- if (gdev->cdev[i]) {
if (gdev->cdev[i]->dev.driver_data == gdev)
gdev->cdev[i]->dev.driver_data = NULL;
put_device(&gdev->cdev[i]->dev);
}
- kfree(gdev);
+ mutex_unlock(&gdev->reg_mutex);
+ put_device(&gdev->dev);
return rc;
}
@@ -422,8 +418,12 @@ ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver)
get_driver(&cdriver->driver);
while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
__ccwgroup_match_all))) {
- __ccwgroup_remove_symlinks(to_ccwgroupdev(dev));
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+
+ mutex_lock(&gdev->reg_mutex);
+ __ccwgroup_remove_symlinks(gdev);
device_unregister(dev);
+ mutex_unlock(&gdev->reg_mutex);
put_device(dev);
}
put_driver(&cdriver->driver);
@@ -444,8 +444,10 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
if (cdev->dev.driver_data) {
gdev = (struct ccwgroup_device *)cdev->dev.driver_data;
if (get_device(&gdev->dev)) {
+ mutex_lock(&gdev->reg_mutex);
if (device_is_registered(&gdev->dev))
return gdev;
+ mutex_unlock(&gdev->reg_mutex);
put_device(&gdev->dev);
}
return NULL;
@@ -465,6 +467,7 @@ ccwgroup_remove_ccwdev(struct ccw_device *cdev)
if (gdev) {
__ccwgroup_remove_symlinks(gdev);
device_unregister(&gdev->dev);
+ mutex_unlock(&gdev->reg_mutex);
put_device(&gdev->dev);
}
}
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
new file mode 100644
index 000000000000..ac289e6eadfe
--- /dev/null
+++ b/drivers/s390/cio/chp.c
@@ -0,0 +1,683 @@
+/*
+ * drivers/s390/cio/chp.c
+ *
+ * Copyright IBM Corp. 1999,2007
+ * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
+ * Arnd Bergmann (arndb@de.ibm.com)
+ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/bug.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <asm/errno.h>
+#include <asm/chpid.h>
+#include <asm/sclp.h>
+
+#include "cio.h"
+#include "css.h"
+#include "ioasm.h"
+#include "cio_debug.h"
+#include "chp.h"
+
+#define to_channelpath(device) container_of(device, struct channel_path, dev)
+#define CHP_INFO_UPDATE_INTERVAL 1*HZ
+
+enum cfg_task_t {
+ cfg_none,
+ cfg_configure,
+ cfg_deconfigure
+};
+
+/* Map for pending configure tasks. */
+static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1];
+static DEFINE_MUTEX(cfg_lock);
+static int cfg_busy;
+
+/* Map for channel-path status. */
+static struct sclp_chp_info chp_info;
+static DEFINE_MUTEX(info_lock);
+
+/* Time after which channel-path status may be outdated. */
+static unsigned long chp_info_expires;
+
+/* Workqueue to perform pending configure tasks. */
+static struct workqueue_struct *chp_wq;
+static struct work_struct cfg_work;
+
+/* Wait queue for configure completion events. */
+static wait_queue_head_t cfg_wait_queue;
+
+/* Return channel_path struct for given chpid. */
+static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
+{
+ return css[chpid.cssid]->chps[chpid.id];
+}
+
+/* Set vary state for given chpid. */
+static void set_chp_logically_online(struct chp_id chpid, int onoff)
+{
+ chpid_to_chp(chpid)->state = onoff;
+}
+
+/* On succes return 0 if channel-path is varied offline, 1 if it is varied
+ * online. Return -ENODEV if channel-path is not registered. */
+int chp_get_status(struct chp_id chpid)
+{
+ return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV);
+}
+
+/**
+ * chp_get_sch_opm - return opm for subchannel
+ * @sch: subchannel
+ *
+ * Calculate and return the operational path mask (opm) based on the chpids
+ * used by the subchannel and the status of the associated channel-paths.
+ */
+u8 chp_get_sch_opm(struct subchannel *sch)
+{
+ struct chp_id chpid;
+ int opm;
+ int i;
+
+ opm = 0;
+ chp_id_init(&chpid);
+ for (i=0; i < 8; i++) {
+ opm <<= 1;
+ chpid.id = sch->schib.pmcw.chpid[i];
+ if (chp_get_status(chpid) != 0)
+ opm |= 1;
+ }
+ return opm;
+}
+
+/**
+ * chp_is_registered - check if a channel-path is registered
+ * @chpid: channel-path ID
+ *
+ * Return non-zero if a channel-path with the given chpid is registered,
+ * zero otherwise.
+ */
+int chp_is_registered(struct chp_id chpid)
+{
+ return chpid_to_chp(chpid) != NULL;
+}
+
+/*
+ * Function: s390_vary_chpid
+ * Varies the specified chpid online or offline
+ */
+static int s390_vary_chpid(struct chp_id chpid, int on)
+{
+ char dbf_text[15];
+ int status;
+
+ sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid,
+ chpid.id);
+ CIO_TRACE_EVENT( 2, dbf_text);
+
+ status = chp_get_status(chpid);
+ if (status < 0) {
+ printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n",
+ chpid.cssid, chpid.id);
+ return -EINVAL;
+ }
+
+ if (!on && !status) {
+ printk(KERN_ERR "chpid %x.%02x is already offline\n",
+ chpid.cssid, chpid.id);
+ return -EINVAL;
+ }
+
+ set_chp_logically_online(chpid, on);
+ chsc_chp_vary(chpid, on);
+ return 0;
+}
+
+/*
+ * Channel measurement related functions
+ */
+static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct channel_path *chp;
+ unsigned int size;
+
+ chp = to_channelpath(container_of(kobj, struct device, kobj));
+ if (!chp->cmg_chars)
+ return 0;
+
+ size = sizeof(struct cmg_chars);
+
+ if (off > size)
+ return 0;
+ if (off + count > size)
+ count = size - off;
+ memcpy(buf, chp->cmg_chars + off, count);
+ return count;
+}
+
+static struct bin_attribute chp_measurement_chars_attr = {
+ .attr = {
+ .name = "measurement_chars",
+ .mode = S_IRUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = sizeof(struct cmg_chars),
+ .read = chp_measurement_chars_read,
+};
+
+static void chp_measurement_copy_block(struct cmg_entry *buf,
+ struct channel_subsystem *css,
+ struct chp_id chpid)
+{
+ void *area;
+ struct cmg_entry *entry, reference_buf;
+ int idx;
+
+ if (chpid.id < 128) {
+ area = css->cub_addr1;
+ idx = chpid.id;
+ } else {
+ area = css->cub_addr2;
+ idx = chpid.id - 128;
+ }
+ entry = area + (idx * sizeof(struct cmg_entry));
+ do {
+ memcpy(buf, entry, sizeof(*entry));
+ memcpy(&reference_buf, entry, sizeof(*entry));
+ } while (reference_buf.values[0] != buf->values[0]);
+}
+
+static ssize_t chp_measurement_read(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct channel_path *chp;
+ struct channel_subsystem *css;
+ unsigned int size;
+
+ chp = to_channelpath(container_of(kobj, struct device, kobj));
+ css = to_css(chp->dev.parent);
+
+ size = sizeof(struct cmg_entry);
+
+ /* Only allow single reads. */
+ if (off || count < size)
+ return 0;
+ chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid);
+ count = size;
+ return count;
+}
+
+static struct bin_attribute chp_measurement_attr = {
+ .attr = {
+ .name = "measurement",
+ .mode = S_IRUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = sizeof(struct cmg_entry),
+ .read = chp_measurement_read,
+};
+
+void chp_remove_cmg_attr(struct channel_path *chp)
+{
+ device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
+ device_remove_bin_file(&chp->dev, &chp_measurement_attr);
+}
+
+int chp_add_cmg_attr(struct channel_path *chp)
+{
+ int ret;
+
+ ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
+ if (ret)
+ return ret;
+ ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
+ if (ret)
+ device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
+ return ret;
+}
+
+/*
+ * Files for the channel path entries.
+ */
+static ssize_t chp_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct channel_path *chp = container_of(dev, struct channel_path, dev);
+
+ if (!chp)
+ return 0;
+ return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") :
+ sprintf(buf, "offline\n"));
+}
+
+static ssize_t chp_status_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct channel_path *cp = container_of(dev, struct channel_path, dev);
+ char cmd[10];
+ int num_args;
+ int error;
+
+ num_args = sscanf(buf, "%5s", cmd);
+ if (!num_args)
+ return count;
+
+ if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1"))
+ error = s390_vary_chpid(cp->chpid, 1);
+ else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0"))
+ error = s390_vary_chpid(cp->chpid, 0);
+ else
+ error = -EINVAL;
+
+ return error < 0 ? error : count;
+
+}
+
+static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
+
+static ssize_t chp_configure_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct channel_path *cp;
+ int status;
+
+ cp = container_of(dev, struct channel_path, dev);
+ status = chp_info_get_status(cp->chpid);
+ if (status < 0)
+ return status;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", status);
+}
+
+static int cfg_wait_idle(void);
+
+static ssize_t chp_configure_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct channel_path *cp;
+ int val;
+ char delim;
+
+ if (sscanf(buf, "%d %c", &val, &delim) != 1)
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+ cp = container_of(dev, struct channel_path, dev);
+ chp_cfg_schedule(cp->chpid, val);
+ cfg_wait_idle();
+
+ return count;
+}
+
+static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write);
+
+static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct channel_path *chp = container_of(dev, struct channel_path, dev);
+
+ if (!chp)
+ return 0;
+ return sprintf(buf, "%x\n", chp->desc.desc);
+}
+
+static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
+
+static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct channel_path *chp = to_channelpath(dev);
+
+ if (!chp)
+ return 0;
+ if (chp->cmg == -1) /* channel measurements not available */
+ return sprintf(buf, "unknown\n");
+ return sprintf(buf, "%x\n", chp->cmg);
+}
+
+static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
+
+static ssize_t chp_shared_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct channel_path *chp = to_channelpath(dev);
+
+ if (!chp)
+ return 0;
+ if (chp->shared == -1) /* channel measurements not available */
+ return sprintf(buf, "unknown\n");
+ return sprintf(buf, "%x\n", chp->shared);
+}
+
+static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
+
+static struct attribute * chp_attrs[] = {
+ &dev_attr_status.attr,
+ &dev_attr_configure.attr,
+ &dev_attr_type.attr,
+ &dev_attr_cmg.attr,
+ &dev_attr_shared.attr,
+ NULL,
+};
+
+static struct attribute_group chp_attr_group = {
+ .attrs = chp_attrs,
+};
+
+static void chp_release(struct device *dev)
+{
+ struct channel_path *cp;
+
+ cp = container_of(dev, struct channel_path, dev);
+ kfree(cp);
+}
+
+/**
+ * chp_new - register a new channel-path
+ * @chpid - channel-path ID
+ *
+ * Create and register data structure representing new channel-path. Return
+ * zero on success, non-zero otherwise.
+ */
+int chp_new(struct chp_id chpid)
+{
+ struct channel_path *chp;
+ int ret;
+
+ if (chp_is_registered(chpid))
+ return 0;
+ chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL);
+ if (!chp)
+ return -ENOMEM;
+
+ /* fill in status, etc. */
+ chp->chpid = chpid;
+ chp->state = 1;
+ chp->dev.parent = &css[chpid.cssid]->device;
+ chp->dev.release = chp_release;
+ snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid,
+ chpid.id);
+
+ /* Obtain channel path description and fill it in. */
+ ret = chsc_determine_channel_path_description(chpid, &chp->desc);
+ if (ret)
+ goto out_free;
+ if ((chp->desc.flags & 0x80) == 0) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+ /* Get channel-measurement characteristics. */
+ if (css_characteristics_avail && css_chsc_characteristics.scmc
+ && css_chsc_characteristics.secm) {
+ ret = chsc_get_channel_measurement_chars(chp);
+ if (ret)
+ goto out_free;
+ } else {
+ static int msg_done;
+
+ if (!msg_done) {
+ printk(KERN_WARNING "cio: Channel measurements not "
+ "available, continuing.\n");
+ msg_done = 1;
+ }
+ chp->cmg = -1;
+ }
+
+ /* make it known to the system */
+ ret = device_register(&chp->dev);
+ if (ret) {
+ printk(KERN_WARNING "%s: could not register %x.%02x\n",
+ __func__, chpid.cssid, chpid.id);
+ goto out_free;
+ }
+ ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
+ if (ret) {
+ device_unregister(&chp->dev);
+ goto out_free;
+ }
+ mutex_lock(&css[chpid.cssid]->mutex);
+ if (css[chpid.cssid]->cm_enabled) {
+ ret = chp_add_cmg_attr(chp);
+ if (ret) {
+ sysfs_remove_group(&chp->dev.kobj, &chp_attr_group);
+ device_unregister(&chp->dev);
+ mutex_unlock(&css[chpid.cssid]->mutex);
+ goto out_free;
+ }
+ }
+ css[chpid.cssid]->chps[chpid.id] = chp;
+ mutex_unlock(&css[chpid.cssid]->mutex);
+ return ret;
+out_free:
+ kfree(chp);
+ return ret;
+}
+
+/**
+ * chp_get_chp_desc - return newly allocated channel-path description
+ * @chpid: channel-path ID
+ *
+ * On success return a newly allocated copy of the channel-path description
+ * data associated with the given channel-path ID. Return %NULL on error.
+ */
+void *chp_get_chp_desc(struct chp_id chpid)
+{
+ struct channel_path *chp;
+ struct channel_path_desc *desc;
+
+ chp = chpid_to_chp(chpid);
+ if (!chp)
+ return NULL;
+ desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
+ if (!desc)
+ return NULL;
+ memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+ return desc;
+}
+
+/**
+ * chp_process_crw - process channel-path status change
+ * @id: channel-path ID number
+ * @status: non-zero if channel-path has become available, zero otherwise
+ *
+ * Handle channel-report-words indicating that the status of a channel-path
+ * has changed.
+ */
+void chp_process_crw(int id, int status)
+{
+ struct chp_id chpid;
+
+ chp_id_init(&chpid);
+ chpid.id = id;
+ if (status) {
+ if (!chp_is_registered(chpid))
+ chp_new(chpid);
+ chsc_chp_online(chpid);
+ } else
+ chsc_chp_offline(chpid);
+}
+
+static inline int info_bit_num(struct chp_id id)
+{
+ return id.id + id.cssid * (__MAX_CHPID + 1);
+}
+
+/* Force chp_info refresh on next call to info_validate(). */
+static void info_expire(void)
+{
+ mutex_lock(&info_lock);
+ chp_info_expires = jiffies - 1;
+ mutex_unlock(&info_lock);
+}
+
+/* Ensure that chp_info is up-to-date. */
+static int info_update(void)
+{
+ int rc;
+
+ mutex_lock(&info_lock);
+ rc = 0;
+ if (time_after(jiffies, chp_info_expires)) {
+ /* Data is too old, update. */
+ rc = sclp_chp_read_info(&chp_info);
+ chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ;
+ }
+ mutex_unlock(&info_lock);
+
+ return rc;
+}
+
+/**
+ * chp_info_get_status - retrieve configure status of a channel-path
+ * @chpid: channel-path ID
+ *
+ * On success, return 0 for standby, 1 for configured, 2 for reserved,
+ * 3 for not recognized. Return negative error code on error.
+ */
+int chp_info_get_status(struct chp_id chpid)
+{
+ int rc;
+ int bit;
+
+ rc = info_update();
+ if (rc)
+ return rc;
+
+ bit = info_bit_num(chpid);
+ mutex_lock(&info_lock);
+ if (!chp_test_bit(chp_info.recognized, bit))
+ rc = CHP_STATUS_NOT_RECOGNIZED;
+ else if (chp_test_bit(chp_info.configured, bit))
+ rc = CHP_STATUS_CONFIGURED;
+ else if (chp_test_bit(chp_info.standby, bit))
+ rc = CHP_STATUS_STANDBY;
+ else
+ rc = CHP_STATUS_RESERVED;
+ mutex_unlock(&info_lock);
+
+ return rc;
+}
+
+/* Return configure task for chpid. */
+static enum cfg_task_t cfg_get_task(struct chp_id chpid)
+{
+ return chp_cfg_task[chpid.cssid][chpid.id];
+}
+
+/* Set configure task for chpid. */
+static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg)
+{
+ chp_cfg_task[chpid.cssid][chpid.id] = cfg;
+}
+
+/* Perform one configure/deconfigure request. Reschedule work function until
+ * last request. */
+static void cfg_func(struct work_struct *work)
+{
+ struct chp_id chpid;
+ enum cfg_task_t t;
+
+ mutex_lock(&cfg_lock);
+ t = cfg_none;
+ chp_id_for_each(&chpid) {
+ t = cfg_get_task(chpid);
+ if (t != cfg_none) {
+ cfg_set_task(chpid, cfg_none);
+ break;
+ }
+ }
+ mutex_unlock(&cfg_lock);
+
+ switch (t) {
+ case cfg_configure:
+ sclp_chp_configure(chpid);
+ info_expire();
+ chsc_chp_online(chpid);
+ break;
+ case cfg_deconfigure:
+ sclp_chp_deconfigure(chpid);
+ info_expire();
+ chsc_chp_offline(chpid);
+ break;
+ case cfg_none:
+ /* Get updated information after last change. */
+ info_update();
+ mutex_lock(&cfg_lock);
+ cfg_busy = 0;
+ mutex_unlock(&cfg_lock);
+ wake_up_interruptible(&cfg_wait_queue);
+ return;
+ }
+ queue_work(chp_wq, &cfg_work);
+}
+
+/**
+ * chp_cfg_schedule - schedule chpid configuration request
+ * @chpid - channel-path ID
+ * @configure - Non-zero for configure, zero for deconfigure
+ *
+ * Schedule a channel-path configuration/deconfiguration request.
+ */
+void chp_cfg_schedule(struct chp_id chpid, int configure)
+{
+ CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id,
+ configure);
+ mutex_lock(&cfg_lock);
+ cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure);
+ cfg_busy = 1;
+ mutex_unlock(&cfg_lock);
+ queue_work(chp_wq, &cfg_work);
+}
+
+/**
+ * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request
+ * @chpid - channel-path ID
+ *
+ * Cancel an active channel-path deconfiguration request if it has not yet
+ * been performed.
+ */
+void chp_cfg_cancel_deconfigure(struct chp_id chpid)
+{
+ CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id);
+ mutex_lock(&cfg_lock);
+ if (cfg_get_task(chpid) == cfg_deconfigure)
+ cfg_set_task(chpid, cfg_none);
+ mutex_unlock(&cfg_lock);
+}
+
+static int cfg_wait_idle(void)
+{
+ if (wait_event_interruptible(cfg_wait_queue, !cfg_busy))
+ return -ERESTARTSYS;
+ return 0;
+}
+
+static int __init chp_init(void)
+{
+ struct chp_id chpid;
+
+ chp_wq = create_singlethread_workqueue("cio_chp");
+ if (!chp_wq)
+ return -ENOMEM;
+ INIT_WORK(&cfg_work, cfg_func);
+ init_waitqueue_head(&cfg_wait_queue);
+ if (info_update())
+ return 0;
+ /* Register available channel-paths. */
+ chp_id_for_each(&chpid) {
+ if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED)
+ chp_new(chpid);
+ }
+
+ return 0;
+}
+
+subsys_initcall(chp_init);
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
new file mode 100644
index 000000000000..65286563c592
--- /dev/null
+++ b/drivers/s390/cio/chp.h
@@ -0,0 +1,53 @@
+/*
+ * drivers/s390/cio/chp.h
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef S390_CHP_H
+#define S390_CHP_H S390_CHP_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "chsc.h"
+
+#define CHP_STATUS_STANDBY 0
+#define CHP_STATUS_CONFIGURED 1
+#define CHP_STATUS_RESERVED 2
+#define CHP_STATUS_NOT_RECOGNIZED 3
+
+static inline int chp_test_bit(u8 *bitmap, int num)
+{
+ int byte = num >> 3;
+ int mask = 128 >> (num & 7);
+
+ return (bitmap[byte] & mask) ? 1 : 0;
+}
+
+
+struct channel_path {
+ struct chp_id chpid;
+ int state;
+ struct channel_path_desc desc;
+ /* Channel-measurement related stuff: */
+ int cmg;
+ int shared;
+ void *cmg_chars;
+ struct device dev;
+};
+
+int chp_get_status(struct chp_id chpid);
+u8 chp_get_sch_opm(struct subchannel *sch);
+int chp_is_registered(struct chp_id chpid);
+void *chp_get_chp_desc(struct chp_id chpid);
+void chp_process_crw(int id, int available);
+void chp_remove_cmg_attr(struct channel_path *chp);
+int chp_add_cmg_attr(struct channel_path *chp);
+int chp_new(struct chp_id chpid);
+void chp_cfg_schedule(struct chp_id chpid, int configure);
+void chp_cfg_cancel_deconfigure(struct chp_id chpid);
+int chp_info_get_status(struct chp_id chpid);
+
+#endif /* S390_CHP_H */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 6f05a44e3817..ea92ac4d6577 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -15,202 +15,124 @@
#include <linux/device.h>
#include <asm/cio.h>
+#include <asm/chpid.h>
#include "css.h"
#include "cio.h"
#include "cio_debug.h"
#include "ioasm.h"
+#include "chp.h"
#include "chsc.h"
static void *sei_page;
-static int new_channel_path(int chpid);
-
-static inline void
-set_chp_logically_online(int chp, int onoff)
-{
- css[0]->chps[chp]->state = onoff;
-}
-
-static int
-get_chp_status(int chp)
-{
- return (css[0]->chps[chp] ? css[0]->chps[chp]->state : -ENODEV);
-}
-
-void
-chsc_validate_chpids(struct subchannel *sch)
-{
- int mask, chp;
-
- for (chp = 0; chp <= 7; chp++) {
- mask = 0x80 >> chp;
- if (!get_chp_status(sch->schib.pmcw.chpid[chp]))
- /* disable using this path */
- sch->opm &= ~mask;
- }
-}
-
-void
-chpid_is_actually_online(int chp)
-{
- int state;
-
- state = get_chp_status(chp);
- if (state < 0) {
- need_rescan = 1;
- queue_work(slow_path_wq, &slow_path_work);
- } else
- WARN_ON(!state);
-}
+struct chsc_ssd_area {
+ struct chsc_header request;
+ u16 :10;
+ u16 ssid:2;
+ u16 :4;
+ u16 f_sch; /* first subchannel */
+ u16 :16;
+ u16 l_sch; /* last subchannel */
+ u32 :32;
+ struct chsc_header response;
+ u32 :32;
+ u8 sch_valid : 1;
+ u8 dev_valid : 1;
+ u8 st : 3; /* subchannel type */
+ u8 zeroes : 3;
+ u8 unit_addr; /* unit address */
+ u16 devno; /* device number */
+ u8 path_mask;
+ u8 fla_valid_mask;
+ u16 sch; /* subchannel */
+ u8 chpid[8]; /* chpids 0-7 */
+ u16 fla[8]; /* full link addresses 0-7 */
+} __attribute__ ((packed));
-/* FIXME: this is _always_ called for every subchannel. shouldn't we
- * process more than one at a time? */
-static int
-chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
+int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
{
- int ccode, j;
-
- struct {
- struct chsc_header request;
- u16 reserved1a:10;
- u16 ssid:2;
- u16 reserved1b:4;
- u16 f_sch; /* first subchannel */
- u16 reserved2;
- u16 l_sch; /* last subchannel */
- u32 reserved3;
- struct chsc_header response;
- u32 reserved4;
- u8 sch_valid : 1;
- u8 dev_valid : 1;
- u8 st : 3; /* subchannel type */
- u8 zeroes : 3;
- u8 unit_addr; /* unit address */
- u16 devno; /* device number */
- u8 path_mask;
- u8 fla_valid_mask;
- u16 sch; /* subchannel */
- u8 chpid[8]; /* chpids 0-7 */
- u16 fla[8]; /* full link addresses 0-7 */
- } __attribute__ ((packed)) *ssd_area;
-
- ssd_area = page;
+ unsigned long page;
+ struct chsc_ssd_area *ssd_area;
+ int ccode;
+ int ret;
+ int i;
+ int mask;
+ page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!page)
+ return -ENOMEM;
+ ssd_area = (struct chsc_ssd_area *) page;
ssd_area->request.length = 0x0010;
ssd_area->request.code = 0x0004;
-
- ssd_area->ssid = sch->schid.ssid;
- ssd_area->f_sch = sch->schid.sch_no;
- ssd_area->l_sch = sch->schid.sch_no;
+ ssd_area->ssid = schid.ssid;
+ ssd_area->f_sch = schid.sch_no;
+ ssd_area->l_sch = schid.sch_no;
ccode = chsc(ssd_area);
+ /* Check response. */
if (ccode > 0) {
- pr_debug("chsc returned with ccode = %d\n", ccode);
- return (ccode == 3) ? -ENODEV : -EBUSY;
+ ret = (ccode == 3) ? -ENODEV : -EBUSY;
+ goto out_free;
}
-
- switch (ssd_area->response.code) {
- case 0x0001: /* everything ok */
- break;
- case 0x0002:
- CIO_CRW_EVENT(2, "Invalid command!\n");
- return -EINVAL;
- case 0x0003:
- CIO_CRW_EVENT(2, "Error in chsc request block!\n");
- return -EINVAL;
- case 0x0004:
- CIO_CRW_EVENT(2, "Model does not provide ssd\n");
- return -EOPNOTSUPP;
- default:
- CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
+ if (ssd_area->response.code != 0x0001) {
+ CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
+ schid.ssid, schid.sch_no,
ssd_area->response.code);
- return -EIO;
+ ret = -EIO;
+ goto out_free;
}
-
- /*
- * ssd_area->st stores the type of the detected
- * subchannel, with the following definitions:
- *
- * 0: I/O subchannel: All fields have meaning
- * 1: CHSC subchannel: Only sch_val, st and sch
- * have meaning
- * 2: Message subchannel: All fields except unit_addr
- * have meaning
- * 3: ADM subchannel: Only sch_val, st and sch
- * have meaning
- *
- * Other types are currently undefined.
- */
- if (ssd_area->st > 3) { /* uhm, that looks strange... */
- CIO_CRW_EVENT(0, "Strange subchannel type %d"
- " for sch 0.%x.%04x\n", ssd_area->st,
- sch->schid.ssid, sch->schid.sch_no);
- /*
- * There may have been a new subchannel type defined in the
- * time since this code was written; since we don't know which
- * fields have meaning and what to do with it we just jump out
- */
- return 0;
- } else {
- const char *type[4] = {"I/O", "chsc", "message", "ADM"};
- CIO_CRW_EVENT(6, "ssd: sch 0.%x.%04x is %s subchannel\n",
- sch->schid.ssid, sch->schid.sch_no,
- type[ssd_area->st]);
-
- sch->ssd_info.valid = 1;
- sch->ssd_info.type = ssd_area->st;
+ if (!ssd_area->sch_valid) {
+ ret = -ENODEV;
+ goto out_free;
}
-
- if (ssd_area->st == 0 || ssd_area->st == 2) {
- for (j = 0; j < 8; j++) {
- if (!((0x80 >> j) & ssd_area->path_mask &
- ssd_area->fla_valid_mask))
- continue;
- sch->ssd_info.chpid[j] = ssd_area->chpid[j];
- sch->ssd_info.fla[j] = ssd_area->fla[j];
+ /* Copy data */
+ ret = 0;
+ memset(ssd, 0, sizeof(struct chsc_ssd_info));
+ if ((ssd_area->st != 0) && (ssd_area->st != 2))
+ goto out_free;
+ ssd->path_mask = ssd_area->path_mask;
+ ssd->fla_valid_mask = ssd_area->fla_valid_mask;
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (ssd_area->path_mask & mask) {
+ chp_id_init(&ssd->chpid[i]);
+ ssd->chpid[i].id = ssd_area->chpid[i];
}
+ if (ssd_area->fla_valid_mask & mask)
+ ssd->fla[i] = ssd_area->fla[i];
}
- return 0;
+out_free:
+ free_page(page);
+ return ret;
}
-int
-css_get_ssd_info(struct subchannel *sch)
+static int check_for_io_on_path(struct subchannel *sch, int mask)
{
- int ret;
- void *page;
+ int cc;
- page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!page)
- return -ENOMEM;
- spin_lock_irq(sch->lock);
- ret = chsc_get_sch_desc_irq(sch, page);
- if (ret) {
- static int cio_chsc_err_msg;
-
- if (!cio_chsc_err_msg) {
- printk(KERN_ERR
- "chsc_get_sch_descriptions:"
- " Error %d while doing chsc; "
- "processing some machine checks may "
- "not work\n", ret);
- cio_chsc_err_msg = 1;
- }
- }
- spin_unlock_irq(sch->lock);
- free_page((unsigned long)page);
- if (!ret) {
- int j, chpid, mask;
- /* Allocate channel path structures, if needed. */
- for (j = 0; j < 8; j++) {
- mask = 0x80 >> j;
- chpid = sch->ssd_info.chpid[j];
- if ((sch->schib.pmcw.pim & mask) &&
- (get_chp_status(chpid) < 0))
- new_channel_path(chpid);
- }
+ cc = stsch(sch->schid, &sch->schib);
+ if (cc)
+ return 0;
+ if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
+ return 1;
+ return 0;
+}
+
+static void terminate_internal_io(struct subchannel *sch)
+{
+ if (cio_clear(sch)) {
+ /* Recheck device in case clear failed. */
+ sch->lpm = 0;
+ if (device_trigger_verify(sch) != 0)
+ css_schedule_eval(sch->schid);
+ return;
}
- return ret;
+ /* Request retry of internal operation. */
+ device_set_intretry(sch);
+ /* Call handler. */
+ if (sch->driver && sch->driver->termination)
+ sch->driver->termination(&sch->dev);
}
static int
@@ -219,7 +141,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
int j;
int mask;
struct subchannel *sch;
- struct channel_path *chpid;
+ struct chp_id *chpid;
struct schib schib;
sch = to_subchannel(dev);
@@ -243,106 +165,50 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
if (sch->schib.pmcw.pim == 0x80)
goto out_unreg;
- if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&
- (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&
- (sch->schib.pmcw.lpum == mask)) {
- int cc;
-
- cc = cio_clear(sch);
- if (cc == -ENODEV)
+ if (check_for_io_on_path(sch, mask)) {
+ if (device_is_online(sch))
+ device_kill_io(sch);
+ else {
+ terminate_internal_io(sch);
+ /* Re-start path verification. */
+ if (sch->driver && sch->driver->verify)
+ sch->driver->verify(&sch->dev);
+ }
+ } else {
+ /* trigger path verification. */
+ if (sch->driver && sch->driver->verify)
+ sch->driver->verify(&sch->dev);
+ else if (sch->lpm == mask)
goto out_unreg;
- /* Request retry of internal operation. */
- device_set_intretry(sch);
- /* Call handler. */
- if (sch->driver && sch->driver->termination)
- sch->driver->termination(&sch->dev);
- goto out_unlock;
}
- /* trigger path verification. */
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
- else if (sch->lpm == mask)
- goto out_unreg;
-out_unlock:
spin_unlock_irq(sch->lock);
return 0;
+
out_unreg:
- spin_unlock_irq(sch->lock);
sch->lpm = 0;
- if (css_enqueue_subchannel_slow(sch->schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- }
+ spin_unlock_irq(sch->lock);
+ css_schedule_eval(sch->schid);
return 0;
}
-static void
-s390_set_chpid_offline( __u8 chpid)
+void chsc_chp_offline(struct chp_id chpid)
{
char dbf_txt[15];
- struct device *dev;
- sprintf(dbf_txt, "chpr%x", chpid);
+ sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);
- if (get_chp_status(chpid) <= 0)
+ if (chp_get_status(chpid) <= 0)
return;
- dev = get_device(&css[0]->chps[chpid]->dev);
- bus_for_each_dev(&css_bus_type, NULL, to_channelpath(dev),
+ bus_for_each_dev(&css_bus_type, NULL, &chpid,
s390_subchannel_remove_chpid);
-
- if (need_rescan || css_slow_subchannels_exist())
- queue_work(slow_path_wq, &slow_path_work);
- put_device(dev);
-}
-
-struct res_acc_data {
- struct channel_path *chp;
- u32 fla_mask;
- u16 fla;
-};
-
-static int
-s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch)
-{
- int found;
- int chp;
- int ccode;
-
- found = 0;
- for (chp = 0; chp <= 7; chp++)
- /*
- * check if chpid is in information updated by ssd
- */
- if (sch->ssd_info.valid &&
- sch->ssd_info.chpid[chp] == res_data->chp->id &&
- (sch->ssd_info.fla[chp] & res_data->fla_mask)
- == res_data->fla) {
- found = 1;
- break;
- }
-
- if (found == 0)
- return 0;
-
- /*
- * Do a stsch to update our subchannel structure with the
- * new path information and eventually check for logically
- * offline chpids.
- */
- ccode = stsch(sch->schid, &sch->schib);
- if (ccode > 0)
- return 0;
-
- return 0x80 >> chp;
}
static int
s390_process_res_acc_new_sch(struct subchannel_id schid)
{
struct schib schib;
- int ret;
/*
* We don't know the device yet, but since a path
* may be available now to the device we'll have
@@ -353,14 +219,35 @@ s390_process_res_acc_new_sch(struct subchannel_id schid)
*/
if (stsch_err(schid, &schib))
/* We're through */
- return need_rescan ? -EAGAIN : -ENXIO;
+ return -ENXIO;
/* Put it on the slow path. */
- ret = css_enqueue_subchannel_slow(schid);
- if (ret) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- return -EAGAIN;
+ css_schedule_eval(schid);
+ return 0;
+}
+
+struct res_acc_data {
+ struct chp_id chpid;
+ u32 fla_mask;
+ u16 fla;
+};
+
+static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
+ struct res_acc_data *data)
+{
+ int i;
+ int mask;
+
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (!(ssd->path_mask & mask))
+ continue;
+ if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
+ continue;
+ if ((ssd->fla_valid_mask & mask) &&
+ ((ssd->fla[i] & data->fla_mask) != data->fla))
+ continue;
+ return mask;
}
return 0;
}
@@ -379,14 +266,11 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
return s390_process_res_acc_new_sch(schid);
spin_lock_irq(sch->lock);
-
- chp_mask = s390_process_res_acc_sch(res_data, sch);
-
- if (chp_mask == 0) {
- spin_unlock_irq(sch->lock);
- put_device(&sch->dev);
- return 0;
- }
+ chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
+ if (chp_mask == 0)
+ goto out;
+ if (stsch(sch->schid, &sch->schib))
+ goto out;
old_lpm = sch->lpm;
sch->lpm = ((sch->schib.pmcw.pim &
sch->schib.pmcw.pam &
@@ -396,20 +280,18 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
-
+out:
spin_unlock_irq(sch->lock);
put_device(&sch->dev);
return 0;
}
-
-static int
-s390_process_res_acc (struct res_acc_data *res_data)
+static void s390_process_res_acc (struct res_acc_data *res_data)
{
- int rc;
char dbf_txt[15];
- sprintf(dbf_txt, "accpr%x", res_data->chp->id);
+ sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
+ res_data->chpid.id);
CIO_TRACE_EVENT( 2, dbf_txt);
if (res_data->fla != 0) {
sprintf(dbf_txt, "fla%x", res_data->fla);
@@ -423,12 +305,7 @@ s390_process_res_acc (struct res_acc_data *res_data)
* The more information we have (info), the less scanning
* will we have to do.
*/
- rc = for_each_subchannel(__s390_process_res_acc, res_data);
- if (css_slow_subchannels_exist())
- rc = -EAGAIN;
- else if (rc != -EAGAIN)
- rc = 0;
- return rc;
+ for_each_subchannel(__s390_process_res_acc, res_data);
}
static int
@@ -480,43 +357,45 @@ struct chsc_sei_area {
/* ccdf has to be big enough for a link-incident record */
} __attribute__ ((packed));
-static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
{
- int chpid;
+ struct chp_id chpid;
+ int id;
CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
sei_area->rs, sei_area->rsid);
if (sei_area->rs != 4)
- return 0;
- chpid = __get_chpid_from_lir(sei_area->ccdf);
- if (chpid < 0)
+ return;
+ id = __get_chpid_from_lir(sei_area->ccdf);
+ if (id < 0)
CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
- else
- s390_set_chpid_offline(chpid);
-
- return 0;
+ else {
+ chp_id_init(&chpid);
+ chpid.id = id;
+ chsc_chp_offline(chpid);
+ }
}
-static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
{
struct res_acc_data res_data;
- struct device *dev;
+ struct chp_id chpid;
int status;
- int rc;
CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
"rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
if (sei_area->rs != 4)
- return 0;
+ return;
+ chp_id_init(&chpid);
+ chpid.id = sei_area->rsid;
/* allocate a new channel path structure, if needed */
- status = get_chp_status(sei_area->rsid);
+ status = chp_get_status(chpid);
if (status < 0)
- new_channel_path(sei_area->rsid);
+ chp_new(chpid);
else if (!status)
- return 0;
- dev = get_device(&css[0]->chps[sei_area->rsid]->dev);
+ return;
memset(&res_data, 0, sizeof(struct res_acc_data));
- res_data.chp = to_channelpath(dev);
+ res_data.chpid = chpid;
if ((sei_area->vf & 0xc0) != 0) {
res_data.fla = sei_area->fla;
if ((sei_area->vf & 0xc0) == 0xc0)
@@ -526,51 +405,82 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
/* link address */
res_data.fla_mask = 0xff00;
}
- rc = s390_process_res_acc(&res_data);
- put_device(dev);
-
- return rc;
+ s390_process_res_acc(&res_data);
}
-static int chsc_process_sei(struct chsc_sei_area *sei_area)
+struct chp_config_data {
+ u8 map[32];
+ u8 op;
+ u8 pc;
+};
+
+static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
{
- int rc;
+ struct chp_config_data *data;
+ struct chp_id chpid;
+ int num;
+
+ CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
+ if (sei_area->rs != 0)
+ return;
+ data = (struct chp_config_data *) &(sei_area->ccdf);
+ chp_id_init(&chpid);
+ for (num = 0; num <= __MAX_CHPID; num++) {
+ if (!chp_test_bit(data->map, num))
+ continue;
+ chpid.id = num;
+ printk(KERN_WARNING "cio: processing configure event %d for "
+ "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
+ switch (data->op) {
+ case 0:
+ chp_cfg_schedule(chpid, 1);
+ break;
+ case 1:
+ chp_cfg_schedule(chpid, 0);
+ break;
+ case 2:
+ chp_cfg_cancel_deconfigure(chpid);
+ break;
+ }
+ }
+}
+static void chsc_process_sei(struct chsc_sei_area *sei_area)
+{
/* Check if we might have lost some information. */
- if (sei_area->flags & 0x40)
+ if (sei_area->flags & 0x40) {
CIO_CRW_EVENT(2, "chsc: event overflow\n");
+ css_schedule_eval_all();
+ }
/* which kind of information was stored? */
- rc = 0;
switch (sei_area->cc) {
case 1: /* link incident*/
- rc = chsc_process_sei_link_incident(sei_area);
+ chsc_process_sei_link_incident(sei_area);
break;
case 2: /* i/o resource accessibiliy */
- rc = chsc_process_sei_res_acc(sei_area);
+ chsc_process_sei_res_acc(sei_area);
+ break;
+ case 8: /* channel-path-configuration notification */
+ chsc_process_sei_chp_config(sei_area);
break;
default: /* other stuff */
CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
sei_area->cc);
break;
}
-
- return rc;
}
-int chsc_process_crw(void)
+void chsc_process_crw(void)
{
struct chsc_sei_area *sei_area;
- int ret;
- int rc;
if (!sei_page)
- return 0;
+ return;
/* Access to sei_page is serialized through machine check handler
* thread, so no need for locking. */
sei_area = sei_page;
CIO_TRACE_EVENT( 2, "prcss");
- ret = 0;
do {
memset(sei_area, 0, sizeof(*sei_area));
sei_area->request.length = 0x0010;
@@ -580,37 +490,26 @@ int chsc_process_crw(void)
if (sei_area->response.code == 0x0001) {
CIO_CRW_EVENT(4, "chsc: sei successful\n");
- rc = chsc_process_sei(sei_area);
- if (rc)
- ret = rc;
+ chsc_process_sei(sei_area);
} else {
CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
sei_area->response.code);
- ret = 0;
break;
}
} while (sei_area->flags & 0x80);
-
- return ret;
}
static int
__chp_add_new_sch(struct subchannel_id schid)
{
struct schib schib;
- int ret;
if (stsch_err(schid, &schib))
/* We're through */
- return need_rescan ? -EAGAIN : -ENXIO;
+ return -ENXIO;
/* Put it on the slow path. */
- ret = css_enqueue_subchannel_slow(schid);
- if (ret) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- return -EAGAIN;
- }
+ css_schedule_eval(schid);
return 0;
}
@@ -619,10 +518,10 @@ static int
__chp_add(struct subchannel_id schid, void *data)
{
int i, mask;
- struct channel_path *chp;
+ struct chp_id *chpid;
struct subchannel *sch;
- chp = data;
+ chpid = data;
sch = get_subchannel_by_schid(schid);
if (!sch)
/* Check if the subchannel is now available. */
@@ -631,7 +530,7 @@ __chp_add(struct subchannel_id schid, void *data)
for (i=0; i<8; i++) {
mask = 0x80 >> i;
if ((sch->schib.pmcw.pim & mask) &&
- (sch->schib.pmcw.chpid[i] == chp->id)) {
+ (sch->schib.pmcw.chpid[i] == chpid->id)) {
if (stsch(sch->schid, &sch->schib) != 0) {
/* Endgame. */
spin_unlock_irq(sch->lock);
@@ -657,122 +556,58 @@ __chp_add(struct subchannel_id schid, void *data)
return 0;
}
-static int
-chp_add(int chpid)
+void chsc_chp_online(struct chp_id chpid)
{
- int rc;
char dbf_txt[15];
- struct device *dev;
- if (!get_chp_status(chpid))
- return 0; /* no need to do the rest */
-
- sprintf(dbf_txt, "cadd%x", chpid);
+ sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);
- dev = get_device(&css[0]->chps[chpid]->dev);
- rc = for_each_subchannel(__chp_add, to_channelpath(dev));
- if (css_slow_subchannels_exist())
- rc = -EAGAIN;
- if (rc != -EAGAIN)
- rc = 0;
- put_device(dev);
- return rc;
+ if (chp_get_status(chpid) != 0)
+ for_each_subchannel(__chp_add, &chpid);
}
-/*
- * Handling of crw machine checks with channel path source.
- */
-int
-chp_process_crw(int chpid, int on)
-{
- if (on == 0) {
- /* Path has gone. We use the link incident routine.*/
- s390_set_chpid_offline(chpid);
- return 0; /* De-register is async anyway. */
- }
- /*
- * Path has come. Allocate a new channel path structure,
- * if needed.
- */
- if (get_chp_status(chpid) < 0)
- new_channel_path(chpid);
- /* Avoid the extra overhead in process_rec_acc. */
- return chp_add(chpid);
-}
-
-static int check_for_io_on_path(struct subchannel *sch, int index)
-{
- int cc;
-
- cc = stsch(sch->schid, &sch->schib);
- if (cc)
- return 0;
- if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index))
- return 1;
- return 0;
-}
-
-static void terminate_internal_io(struct subchannel *sch)
-{
- if (cio_clear(sch)) {
- /* Recheck device in case clear failed. */
- sch->lpm = 0;
- if (device_trigger_verify(sch) != 0) {
- if(css_enqueue_subchannel_slow(sch->schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- }
- }
- return;
- }
- /* Request retry of internal operation. */
- device_set_intretry(sch);
- /* Call handler. */
- if (sch->driver && sch->driver->termination)
- sch->driver->termination(&sch->dev);
-}
-
-static void
-__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
+static void __s390_subchannel_vary_chpid(struct subchannel *sch,
+ struct chp_id chpid, int on)
{
int chp, old_lpm;
+ int mask;
unsigned long flags;
- if (!sch->ssd_info.valid)
- return;
-
spin_lock_irqsave(sch->lock, flags);
old_lpm = sch->lpm;
for (chp = 0; chp < 8; chp++) {
- if (sch->ssd_info.chpid[chp] != chpid)
+ mask = 0x80 >> chp;
+ if (!(sch->ssd_info.path_mask & mask))
+ continue;
+ if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
continue;
if (on) {
- sch->opm |= (0x80 >> chp);
- sch->lpm |= (0x80 >> chp);
+ sch->opm |= mask;
+ sch->lpm |= mask;
if (!old_lpm)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
break;
}
- sch->opm &= ~(0x80 >> chp);
- sch->lpm &= ~(0x80 >> chp);
- if (check_for_io_on_path(sch, chp)) {
+ sch->opm &= ~mask;
+ sch->lpm &= ~mask;
+ if (check_for_io_on_path(sch, mask)) {
if (device_is_online(sch))
/* Path verification is done after killing. */
device_kill_io(sch);
- else
+ else {
/* Kill and retry internal I/O. */
terminate_internal_io(sch);
- } else if (!sch->lpm) {
- if (device_trigger_verify(sch) != 0) {
- if (css_enqueue_subchannel_slow(sch->schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- }
+ /* Re-start path verification. */
+ if (sch->driver && sch->driver->verify)
+ sch->driver->verify(&sch->dev);
}
+ } else if (!sch->lpm) {
+ if (device_trigger_verify(sch) != 0)
+ css_schedule_eval(sch->schid);
} else if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
break;
@@ -780,11 +615,10 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
spin_unlock_irqrestore(sch->lock, flags);
}
-static int
-s390_subchannel_vary_chpid_off(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
{
struct subchannel *sch;
- __u8 *chpid;
+ struct chp_id *chpid;
sch = to_subchannel(dev);
chpid = data;
@@ -793,11 +627,10 @@ s390_subchannel_vary_chpid_off(struct device *dev, void *data)
return 0;
}
-static int
-s390_subchannel_vary_chpid_on(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
{
struct subchannel *sch;
- __u8 *chpid;
+ struct chp_id *chpid;
sch = to_subchannel(dev);
chpid = data;
@@ -821,40 +654,17 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
/* We're through */
return -ENXIO;
/* Put it on the slow path. */
- if (css_enqueue_subchannel_slow(schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- return -EAGAIN;
- }
+ css_schedule_eval(schid);
return 0;
}
-/*
- * Function: s390_vary_chpid
- * Varies the specified chpid online or offline
+/**
+ * chsc_chp_vary - propagate channel-path vary operation to subchannels
+ * @chpid: channl-path ID
+ * @on: non-zero for vary online, zero for vary offline
*/
-static int
-s390_vary_chpid( __u8 chpid, int on)
+int chsc_chp_vary(struct chp_id chpid, int on)
{
- char dbf_text[15];
- int status;
-
- sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid);
- CIO_TRACE_EVENT( 2, dbf_text);
-
- status = get_chp_status(chpid);
- if (status < 0) {
- printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid);
- return -EINVAL;
- }
-
- if (!on && !status) {
- printk(KERN_ERR "chpid %x is already offline\n", chpid);
- return -EINVAL;
- }
-
- set_chp_logically_online(chpid, on);
-
/*
* Redo PathVerification on the devices the chpid connects to
*/
@@ -865,118 +675,9 @@ s390_vary_chpid( __u8 chpid, int on)
if (on)
/* Scan for new devices on varied on path. */
for_each_subchannel(__s390_vary_chpid_on, NULL);
- if (need_rescan || css_slow_subchannels_exist())
- queue_work(slow_path_wq, &slow_path_work);
return 0;
}
-/*
- * Channel measurement related functions
- */
-static ssize_t
-chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off,
- size_t count)
-{
- struct channel_path *chp;
- unsigned int size;
-
- chp = to_channelpath(container_of(kobj, struct device, kobj));
- if (!chp->cmg_chars)
- return 0;
-
- size = sizeof(struct cmg_chars);
-
- if (off > size)
- return 0;
- if (off + count > size)
- count = size - off;
- memcpy(buf, chp->cmg_chars + off, count);
- return count;
-}
-
-static struct bin_attribute chp_measurement_chars_attr = {
- .attr = {
- .name = "measurement_chars",
- .mode = S_IRUSR,
- .owner = THIS_MODULE,
- },
- .size = sizeof(struct cmg_chars),
- .read = chp_measurement_chars_read,
-};
-
-static void
-chp_measurement_copy_block(struct cmg_entry *buf,
- struct channel_subsystem *css, int chpid)
-{
- void *area;
- struct cmg_entry *entry, reference_buf;
- int idx;
-
- if (chpid < 128) {
- area = css->cub_addr1;
- idx = chpid;
- } else {
- area = css->cub_addr2;
- idx = chpid - 128;
- }
- entry = area + (idx * sizeof(struct cmg_entry));
- do {
- memcpy(buf, entry, sizeof(*entry));
- memcpy(&reference_buf, entry, sizeof(*entry));
- } while (reference_buf.values[0] != buf->values[0]);
-}
-
-static ssize_t
-chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
-{
- struct channel_path *chp;
- struct channel_subsystem *css;
- unsigned int size;
-
- chp = to_channelpath(container_of(kobj, struct device, kobj));
- css = to_css(chp->dev.parent);
-
- size = sizeof(struct cmg_entry);
-
- /* Only allow single reads. */
- if (off || count < size)
- return 0;
- chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id);
- count = size;
- return count;
-}
-
-static struct bin_attribute chp_measurement_attr = {
- .attr = {
- .name = "measurement",
- .mode = S_IRUSR,
- .owner = THIS_MODULE,
- },
- .size = sizeof(struct cmg_entry),
- .read = chp_measurement_read,
-};
-
-static void
-chsc_remove_chp_cmg_attr(struct channel_path *chp)
-{
- device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
- device_remove_bin_file(&chp->dev, &chp_measurement_attr);
-}
-
-static int
-chsc_add_chp_cmg_attr(struct channel_path *chp)
-{
- int ret;
-
- ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
- if (ret)
- return ret;
- ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
- if (ret)
- device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
- return ret;
-}
-
static void
chsc_remove_cmg_attr(struct channel_subsystem *css)
{
@@ -985,7 +686,7 @@ chsc_remove_cmg_attr(struct channel_subsystem *css)
for (i = 0; i <= __MAX_CHPID; i++) {
if (!css->chps[i])
continue;
- chsc_remove_chp_cmg_attr(css->chps[i]);
+ chp_remove_cmg_attr(css->chps[i]);
}
}
@@ -998,7 +699,7 @@ chsc_add_cmg_attr(struct channel_subsystem *css)
for (i = 0; i <= __MAX_CHPID; i++) {
if (!css->chps[i])
continue;
- ret = chsc_add_chp_cmg_attr(css->chps[i]);
+ ret = chp_add_cmg_attr(css->chps[i]);
if (ret)
goto cleanup;
}
@@ -1007,12 +708,11 @@ cleanup:
for (--i; i >= 0; i--) {
if (!css->chps[i])
continue;
- chsc_remove_chp_cmg_attr(css->chps[i]);
+ chp_remove_cmg_attr(css->chps[i]);
}
return ret;
}
-
static int
__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
{
@@ -1118,7 +818,7 @@ chsc_secm(struct channel_subsystem *css, int enable)
} else
chsc_remove_cmg_attr(css);
}
- if (enable && !css->cm_enabled) {
+ if (!css->cm_enabled) {
free_page((unsigned long)css->cub_addr1);
free_page((unsigned long)css->cub_addr2);
}
@@ -1127,109 +827,8 @@ chsc_secm(struct channel_subsystem *css, int enable)
return ret;
}
-/*
- * Files for the channel path entries.
- */
-static ssize_t
-chp_status_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct channel_path *chp = container_of(dev, struct channel_path, dev);
-
- if (!chp)
- return 0;
- return (get_chp_status(chp->id) ? sprintf(buf, "online\n") :
- sprintf(buf, "offline\n"));
-}
-
-static ssize_t
-chp_status_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct channel_path *cp = container_of(dev, struct channel_path, dev);
- char cmd[10];
- int num_args;
- int error;
-
- num_args = sscanf(buf, "%5s", cmd);
- if (!num_args)
- return count;
-
- if (!strnicmp(cmd, "on", 2))
- error = s390_vary_chpid(cp->id, 1);
- else if (!strnicmp(cmd, "off", 3))
- error = s390_vary_chpid(cp->id, 0);
- else
- error = -EINVAL;
-
- return error < 0 ? error : count;
-
-}
-
-static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
-
-static ssize_t
-chp_type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct channel_path *chp = container_of(dev, struct channel_path, dev);
-
- if (!chp)
- return 0;
- return sprintf(buf, "%x\n", chp->desc.desc);
-}
-
-static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
-
-static ssize_t
-chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct channel_path *chp = to_channelpath(dev);
-
- if (!chp)
- return 0;
- if (chp->cmg == -1) /* channel measurements not available */
- return sprintf(buf, "unknown\n");
- return sprintf(buf, "%x\n", chp->cmg);
-}
-
-static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
-
-static ssize_t
-chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct channel_path *chp = to_channelpath(dev);
-
- if (!chp)
- return 0;
- if (chp->shared == -1) /* channel measurements not available */
- return sprintf(buf, "unknown\n");
- return sprintf(buf, "%x\n", chp->shared);
-}
-
-static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
-
-static struct attribute * chp_attrs[] = {
- &dev_attr_status.attr,
- &dev_attr_type.attr,
- &dev_attr_cmg.attr,
- &dev_attr_shared.attr,
- NULL,
-};
-
-static struct attribute_group chp_attr_group = {
- .attrs = chp_attrs,
-};
-
-static void
-chp_release(struct device *dev)
-{
- struct channel_path *cp;
-
- cp = container_of(dev, struct channel_path, dev);
- kfree(cp);
-}
-
-static int
-chsc_determine_channel_path_description(int chpid,
- struct channel_path_desc *desc)
+int chsc_determine_channel_path_description(struct chp_id chpid,
+ struct channel_path_desc *desc)
{
int ccode, ret;
@@ -1252,8 +851,8 @@ chsc_determine_channel_path_description(int chpid,
scpd_area->request.length = 0x0010;
scpd_area->request.code = 0x0002;
- scpd_area->first_chpid = chpid;
- scpd_area->last_chpid = chpid;
+ scpd_area->first_chpid = chpid.id;
+ scpd_area->last_chpid = chpid.id;
ccode = chsc(scpd_area);
if (ccode > 0) {
@@ -1316,8 +915,7 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
}
}
-static int
-chsc_get_channel_measurement_chars(struct channel_path *chp)
+int chsc_get_channel_measurement_chars(struct channel_path *chp)
{
int ccode, ret;
@@ -1349,8 +947,8 @@ chsc_get_channel_measurement_chars(struct channel_path *chp)
scmc_area->request.length = 0x0010;
scmc_area->request.code = 0x0022;
- scmc_area->first_chpid = chp->id;
- scmc_area->last_chpid = chp->id;
+ scmc_area->first_chpid = chp->chpid.id;
+ scmc_area->last_chpid = chp->chpid.id;
ccode = chsc(scmc_area);
if (ccode > 0) {
@@ -1392,94 +990,6 @@ out:
return ret;
}
-/*
- * Entries for chpids on the system bus.
- * This replaces /proc/chpids.
- */
-static int
-new_channel_path(int chpid)
-{
- struct channel_path *chp;
- int ret;
-
- chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL);
- if (!chp)
- return -ENOMEM;
-
- /* fill in status, etc. */
- chp->id = chpid;
- chp->state = 1;
- chp->dev.parent = &css[0]->device;
- chp->dev.release = chp_release;
- snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid);
-
- /* Obtain channel path description and fill it in. */
- ret = chsc_determine_channel_path_description(chpid, &chp->desc);
- if (ret)
- goto out_free;
- /* Get channel-measurement characteristics. */
- if (css_characteristics_avail && css_chsc_characteristics.scmc
- && css_chsc_characteristics.secm) {
- ret = chsc_get_channel_measurement_chars(chp);
- if (ret)
- goto out_free;
- } else {
- static int msg_done;
-
- if (!msg_done) {
- printk(KERN_WARNING "cio: Channel measurements not "
- "available, continuing.\n");
- msg_done = 1;
- }
- chp->cmg = -1;
- }
-
- /* make it known to the system */
- ret = device_register(&chp->dev);
- if (ret) {
- printk(KERN_WARNING "%s: could not register %02x\n",
- __func__, chpid);
- goto out_free;
- }
- ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
- if (ret) {
- device_unregister(&chp->dev);
- goto out_free;
- }
- mutex_lock(&css[0]->mutex);
- if (css[0]->cm_enabled) {
- ret = chsc_add_chp_cmg_attr(chp);
- if (ret) {
- sysfs_remove_group(&chp->dev.kobj, &chp_attr_group);
- device_unregister(&chp->dev);
- mutex_unlock(&css[0]->mutex);
- goto out_free;
- }
- }
- css[0]->chps[chpid] = chp;
- mutex_unlock(&css[0]->mutex);
- return ret;
-out_free:
- kfree(chp);
- return ret;
-}
-
-void *
-chsc_get_chp_desc(struct subchannel *sch, int chp_no)
-{
- struct channel_path *chp;
- struct channel_path_desc *desc;
-
- chp = css[0]->chps[sch->schib.pmcw.chpid[chp_no]];
- if (!chp)
- return NULL;
- desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
- if (!desc)
- return NULL;
- memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
- return desc;
-}
-
static int __init
chsc_alloc_sei_area(void)
{
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 0fb2b024208f..2ad81d11cf7b 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -1,9 +1,10 @@
#ifndef S390_CHSC_H
#define S390_CHSC_H
-#define CHSC_SEI_ACC_CHPID 1
-#define CHSC_SEI_ACC_LINKADDR 2
-#define CHSC_SEI_ACC_FULLLINKADDR 3
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "schid.h"
#define CHSC_SDA_OC_MSS 0x2
@@ -33,23 +34,9 @@ struct channel_path_desc {
u8 chpp;
} __attribute__ ((packed));
-struct channel_path {
- int id;
- int state;
- struct channel_path_desc desc;
- /* Channel-measurement related stuff: */
- int cmg;
- int shared;
- void *cmg_chars;
- struct device dev;
-};
+struct channel_path;
-extern void s390_process_css( void );
-extern void chsc_validate_chpids(struct subchannel *);
-extern void chpid_is_actually_online(int);
-extern int css_get_ssd_info(struct subchannel *);
-extern int chsc_process_crw(void);
-extern int chp_process_crw(int, int);
+extern void chsc_process_crw(void);
struct css_general_char {
u64 : 41;
@@ -82,15 +69,26 @@ struct css_chsc_char {
extern struct css_general_char css_general_characteristics;
extern struct css_chsc_char css_chsc_characteristics;
+struct chsc_ssd_info {
+ u8 path_mask;
+ u8 fla_valid_mask;
+ struct chp_id chpid[8];
+ u16 fla[8];
+};
+extern int chsc_get_ssd_info(struct subchannel_id schid,
+ struct chsc_ssd_info *ssd);
extern int chsc_determine_css_characteristics(void);
extern int css_characteristics_avail;
-extern void *chsc_get_chp_desc(struct subchannel*, int);
-
extern int chsc_enable_facility(int);
struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int);
-#define to_channelpath(device) container_of(device, struct channel_path, dev)
+int chsc_chp_vary(struct chp_id chpid, int on);
+int chsc_determine_channel_path_description(struct chp_id chpid,
+ struct channel_path_desc *desc);
+void chsc_chp_online(struct chp_id chpid);
+void chsc_chp_offline(struct chp_id chpid);
+int chsc_get_channel_measurement_chars(struct channel_path *chp);
#endif
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 9cb129ab5be5..ea1defba5693 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -22,6 +22,7 @@
#include <asm/setup.h>
#include <asm/reset.h>
#include <asm/ipl.h>
+#include <asm/chpid.h>
#include "airq.h"
#include "cio.h"
#include "css.h"
@@ -29,6 +30,7 @@
#include "ioasm.h"
#include "blacklist.h"
#include "cio_debug.h"
+#include "chp.h"
#include "../s390mach.h"
debug_info_t *cio_debug_msg_id;
@@ -592,9 +594,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
err = -ENODEV;
goto out;
}
- sch->opm = 0xff;
- if (!cio_is_console(sch->schid))
- chsc_validate_chpids(sch);
+ if (cio_is_console(sch->schid))
+ sch->opm = 0xff;
+ else
+ sch->opm = chp_get_sch_opm(sch);
sch->lpm = sch->schib.pmcw.pam & sch->opm;
CIO_DEBUG(KERN_INFO, 0,
@@ -954,6 +957,7 @@ static void css_reset(void)
{
int i, ret;
unsigned long long timeout;
+ struct chp_id chpid;
/* Reset subchannels. */
for_each_subchannel(__shutdown_subchannel_easy, NULL);
@@ -963,8 +967,10 @@ static void css_reset(void)
__ctl_set_bit(14, 28);
/* Temporarily reenable machine checks. */
local_mcck_enable();
+ chp_id_init(&chpid);
for (i = 0; i <= __MAX_CHPID; i++) {
- ret = rchp(i);
+ chpid.id = i;
+ ret = rchp(chpid);
if ((ret == 0) || (ret == 2))
/*
* rchp either succeeded, or another rchp is already
@@ -1048,37 +1054,19 @@ void reipl_ccw_dev(struct ccw_dev_id *devid)
do_reipl_asm(*((__u32*)&schid));
}
-static struct schib __initdata ipl_schib;
-
-/*
- * ipl_save_parameters gets called very early. It is not allowed to access
- * anything in the bss section at all. The bss section is not cleared yet,
- * but may contain some ipl parameters written by the firmware.
- * These parameters (if present) are copied to 0x2000.
- * To avoid corruption of the ipl parameters, all variables used by this
- * function must reside on the stack or in the data section.
- */
-void ipl_save_parameters(void)
+int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
{
struct subchannel_id schid;
- unsigned int *ipl_ptr;
- void *src, *dst;
+ struct schib schib;
schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID;
if (!schid.one)
- return;
- if (stsch(schid, &ipl_schib))
- return;
- if (!ipl_schib.pmcw.dnv)
- return;
- ipl_devno = ipl_schib.pmcw.dev;
- ipl_flags |= IPL_DEVNO_VALID;
- if (!ipl_schib.pmcw.qf)
- return;
- ipl_flags |= IPL_PARMBLOCK_VALID;
- ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR;
- src = (void *)(unsigned long)*ipl_ptr;
- dst = (void *)IPL_PARMBLOCK_ORIGIN;
- memmove(dst, src, PAGE_SIZE);
- *ipl_ptr = IPL_PARMBLOCK_ORIGIN;
+ return -ENODEV;
+ if (stsch(schid, &schib))
+ return -ENODEV;
+ if (!schib.pmcw.dnv)
+ return -ENODEV;
+ iplinfo->devno = schib.pmcw.dev;
+ iplinfo->is_qdio = schib.pmcw.qf;
+ return 0;
}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 35154a210357..7446c39951a7 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -1,18 +1,11 @@
#ifndef S390_CIO_H
#define S390_CIO_H
-#include "schid.h"
#include <linux/mutex.h>
-
-/*
- * where we put the ssd info
- */
-struct ssd_info {
- __u8 valid:1;
- __u8 type:7; /* subchannel type */
- __u8 chpid[8]; /* chpids */
- __u16 fla[8]; /* full link addresses */
-} __attribute__ ((packed));
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "chsc.h"
+#include "schid.h"
/*
* path management control word
@@ -108,7 +101,7 @@ struct subchannel {
struct schib schib; /* subchannel information block */
struct orb orb; /* operation request block */
struct ccw1 sense_ccw; /* static ccw for sense command */
- struct ssd_info ssd_info; /* subchannel description */
+ struct chsc_ssd_info ssd_info; /* subchannel description */
struct device dev; /* entry in device tree */
struct css_driver *driver;
} __attribute__ ((aligned(8)));
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 90b22faabbf7..28abd697be1a 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -476,7 +476,7 @@ struct cmb_area {
};
static struct cmb_area cmb_area = {
- .lock = SPIN_LOCK_UNLOCKED,
+ .lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock),
.list = LIST_HEAD_INIT(cmb_area.list),
.num_channels = 1024,
};
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index fe0ace7aece8..27c6d9e55b23 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -20,8 +20,9 @@
#include "ioasm.h"
#include "chsc.h"
#include "device.h"
+#include "idset.h"
+#include "chp.h"
-int need_rescan = 0;
int css_init_done = 0;
static int need_reprobe = 0;
static int max_ssid = 0;
@@ -125,8 +126,52 @@ void css_sch_device_unregister(struct subchannel *sch)
mutex_unlock(&sch->reg_mutex);
}
-static int
-css_register_subchannel(struct subchannel *sch)
+static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
+{
+ int i;
+ int mask;
+
+ memset(ssd, 0, sizeof(struct chsc_ssd_info));
+ ssd->path_mask = pmcw->pim;
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (pmcw->pim & mask) {
+ chp_id_init(&ssd->chpid[i]);
+ ssd->chpid[i].id = pmcw->chpid[i];
+ }
+ }
+}
+
+static void ssd_register_chpids(struct chsc_ssd_info *ssd)
+{
+ int i;
+ int mask;
+
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (ssd->path_mask & mask)
+ if (!chp_is_registered(ssd->chpid[i]))
+ chp_new(ssd->chpid[i]);
+ }
+}
+
+void css_update_ssd_info(struct subchannel *sch)
+{
+ int ret;
+
+ if (cio_is_console(sch->schid)) {
+ /* Console is initialized too early for functions requiring
+ * memory allocation. */
+ ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+ } else {
+ ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info);
+ if (ret)
+ ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+ ssd_register_chpids(&sch->ssd_info);
+ }
+}
+
+static int css_register_subchannel(struct subchannel *sch)
{
int ret;
@@ -135,9 +180,7 @@ css_register_subchannel(struct subchannel *sch)
sch->dev.bus = &css_bus_type;
sch->dev.release = &css_subchannel_release;
sch->dev.groups = subch_attr_groups;
-
- css_get_ssd_info(sch);
-
+ css_update_ssd_info(sch);
/* make it known to the system */
ret = css_sch_device_register(sch);
if (ret) {
@@ -306,7 +349,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
return css_probe_device(schid);
}
-static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
+static void css_evaluate_subchannel(struct subchannel_id schid, int slow)
{
struct subchannel *sch;
int ret;
@@ -317,53 +360,66 @@ static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
put_device(&sch->dev);
} else
ret = css_evaluate_new_subchannel(schid, slow);
-
- return ret;
+ if (ret == -EAGAIN)
+ css_schedule_eval(schid);
}
-static int
-css_rescan_devices(struct subchannel_id schid, void *data)
+static struct idset *slow_subchannel_set;
+static spinlock_t slow_subchannel_lock;
+
+static int __init slow_subchannel_init(void)
{
- return css_evaluate_subchannel(schid, 1);
+ spin_lock_init(&slow_subchannel_lock);
+ slow_subchannel_set = idset_sch_new();
+ if (!slow_subchannel_set) {
+ printk(KERN_WARNING "cio: could not allocate slow subchannel "
+ "set\n");
+ return -ENOMEM;
+ }
+ return 0;
}
-struct slow_subchannel {
- struct list_head slow_list;
- struct subchannel_id schid;
-};
-
-static LIST_HEAD(slow_subchannels_head);
-static DEFINE_SPINLOCK(slow_subchannel_lock);
+subsys_initcall(slow_subchannel_init);
-static void
-css_trigger_slow_path(struct work_struct *unused)
+static void css_slow_path_func(struct work_struct *unused)
{
- CIO_TRACE_EVENT(4, "slowpath");
-
- if (need_rescan) {
- need_rescan = 0;
- for_each_subchannel(css_rescan_devices, NULL);
- return;
- }
+ struct subchannel_id schid;
+ CIO_TRACE_EVENT(4, "slowpath");
spin_lock_irq(&slow_subchannel_lock);
- while (!list_empty(&slow_subchannels_head)) {
- struct slow_subchannel *slow_sch =
- list_entry(slow_subchannels_head.next,
- struct slow_subchannel, slow_list);
-
- list_del_init(slow_subchannels_head.next);
+ init_subchannel_id(&schid);
+ while (idset_sch_get_first(slow_subchannel_set, &schid)) {
+ idset_sch_del(slow_subchannel_set, schid);
spin_unlock_irq(&slow_subchannel_lock);
- css_evaluate_subchannel(slow_sch->schid, 1);
+ css_evaluate_subchannel(schid, 1);
spin_lock_irq(&slow_subchannel_lock);
- kfree(slow_sch);
}
spin_unlock_irq(&slow_subchannel_lock);
}
-DECLARE_WORK(slow_path_work, css_trigger_slow_path);
+static DECLARE_WORK(slow_path_work, css_slow_path_func);
struct workqueue_struct *slow_path_wq;
+void css_schedule_eval(struct subchannel_id schid)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&slow_subchannel_lock, flags);
+ idset_sch_add(slow_subchannel_set, schid);
+ queue_work(slow_path_wq, &slow_path_work);
+ spin_unlock_irqrestore(&slow_subchannel_lock, flags);
+}
+
+void css_schedule_eval_all(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&slow_subchannel_lock, flags);
+ idset_fill(slow_subchannel_set);
+ queue_work(slow_path_wq, &slow_path_work);
+ spin_unlock_irqrestore(&slow_subchannel_lock, flags);
+}
+
/* Reprobe subchannel if unregistered. */
static int reprobe_subchannel(struct subchannel_id schid, void *data)
{
@@ -426,33 +482,14 @@ void css_schedule_reprobe(void)
EXPORT_SYMBOL_GPL(css_schedule_reprobe);
/*
- * Rescan for new devices. FIXME: This is slow.
- * This function is called when we have lost CRWs due to overflows and we have
- * to do subchannel housekeeping.
- */
-void
-css_reiterate_subchannels(void)
-{
- css_clear_subchannel_slow_list();
- need_rescan = 1;
-}
-
-/*
* Called from the machine check handler for subchannel report words.
*/
-int
-css_process_crw(int rsid1, int rsid2)
+void css_process_crw(int rsid1, int rsid2)
{
- int ret;
struct subchannel_id mchk_schid;
CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
rsid1, rsid2);
-
- if (need_rescan)
- /* We need to iterate all subchannels anyway. */
- return -EAGAIN;
-
init_subchannel_id(&mchk_schid);
mchk_schid.sch_no = rsid1;
if (rsid2 != 0)
@@ -463,14 +500,7 @@ css_process_crw(int rsid1, int rsid2)
* use stsch() to find out if the subchannel in question has come
* or gone.
*/
- ret = css_evaluate_subchannel(mchk_schid, 0);
- if (ret == -EAGAIN) {
- if (css_enqueue_subchannel_slow(mchk_schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- }
- }
- return ret;
+ css_evaluate_subchannel(mchk_schid, 0);
}
static int __init
@@ -745,47 +775,6 @@ struct bus_type css_bus_type = {
subsys_initcall(init_channel_subsystem);
-int
-css_enqueue_subchannel_slow(struct subchannel_id schid)
-{
- struct slow_subchannel *new_slow_sch;
- unsigned long flags;
-
- new_slow_sch = kzalloc(sizeof(struct slow_subchannel), GFP_ATOMIC);
- if (!new_slow_sch)
- return -ENOMEM;
- new_slow_sch->schid = schid;
- spin_lock_irqsave(&slow_subchannel_lock, flags);
- list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head);
- spin_unlock_irqrestore(&slow_subchannel_lock, flags);
- return 0;
-}
-
-void
-css_clear_subchannel_slow_list(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&slow_subchannel_lock, flags);
- while (!list_empty(&slow_subchannels_head)) {
- struct slow_subchannel *slow_sch =
- list_entry(slow_subchannels_head.next,
- struct slow_subchannel, slow_list);
-
- list_del_init(slow_subchannels_head.next);
- kfree(slow_sch);
- }
- spin_unlock_irqrestore(&slow_subchannel_lock, flags);
-}
-
-
-
-int
-css_slow_subchannels_exist(void)
-{
- return (!list_empty(&slow_subchannels_head));
-}
-
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(css_bus_type);
EXPORT_SYMBOL_GPL(css_characteristics_avail);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index ca2bab932a8a..71fcfdc42800 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -4,8 +4,11 @@
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/types.h>
#include <asm/cio.h>
+#include <asm/chpid.h>
#include "schid.h"
@@ -143,13 +146,12 @@ extern void css_sch_device_unregister(struct subchannel *);
extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
extern int css_init_done;
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
-extern int css_process_crw(int, int);
+extern void css_process_crw(int, int);
extern void css_reiterate_subchannels(void);
+void css_update_ssd_info(struct subchannel *sch);
#define __MAX_SUBCHANNEL 65535
#define __MAX_SSID 3
-#define __MAX_CHPID 255
-#define __MAX_CSSID 0
struct channel_subsystem {
u8 cssid;
@@ -185,16 +187,12 @@ int device_trigger_verify(struct subchannel *sch);
void device_kill_pending_timer(struct subchannel *);
/* Helper functions to build lists for the slow path. */
-extern int css_enqueue_subchannel_slow(struct subchannel_id schid);
-void css_walk_subchannel_slow_list(void (*fn)(unsigned long));
-void css_clear_subchannel_slow_list(void);
-int css_slow_subchannels_exist(void);
-extern int need_rescan;
+void css_schedule_eval(struct subchannel_id schid);
+void css_schedule_eval_all(void);
int sch_is_pseudo_sch(struct subchannel *);
extern struct workqueue_struct *slow_path_wq;
-extern struct work_struct slow_path_work;
int subchannel_add_files (struct device *);
extern struct attribute_group *subch_attr_groups[];
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index e322111fb369..a23ff582db9d 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -56,13 +56,12 @@ ccw_bus_match (struct device * dev, struct device_driver * drv)
/* Store modalias string delimited by prefix/suffix string into buffer with
* specified size. Return length of resulting string (excluding trailing '\0')
* even if string doesn't fit buffer (snprintf semantics). */
-static int snprint_alias(char *buf, size_t size, const char *prefix,
+static int snprint_alias(char *buf, size_t size,
struct ccw_device_id *id, const char *suffix)
{
int len;
- len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type,
- id->cu_model);
+ len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model);
if (len > size)
return len;
buf += len;
@@ -85,53 +84,40 @@ static int ccw_uevent(struct device *dev, char **envp, int num_envp,
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_device_id *id = &(cdev->id);
int i = 0;
- int len;
+ int len = 0;
+ int ret;
+ char modalias_buf[30];
/* CU_TYPE= */
- len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "CU_TYPE=%04X", id->cu_type);
+ if (ret)
+ return ret;
/* CU_MODEL= */
- len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "CU_MODEL=%02X", id->cu_model);
+ if (ret)
+ return ret;
/* The next two can be zero, that's ok for us */
/* DEV_TYPE= */
- len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "DEV_TYPE=%04X", id->dev_type);
+ if (ret)
+ return ret;
/* DEV_MODEL= */
- len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X",
- (unsigned char) id->dev_model) + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "DEV_MODEL=%02X", id->dev_model);
+ if (ret)
+ return ret;
/* MODALIAS= */
- len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
-
- envp[i] = NULL;
-
- return 0;
+ snprint_alias(modalias_buf, sizeof(modalias_buf), id, "");
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "MODALIAS=%s", modalias_buf);
+ return ret;
}
struct bus_type ccw_bus_type;
@@ -230,12 +216,18 @@ static ssize_t
chpids_show (struct device * dev, struct device_attribute *attr, char * buf)
{
struct subchannel *sch = to_subchannel(dev);
- struct ssd_info *ssd = &sch->ssd_info;
+ struct chsc_ssd_info *ssd = &sch->ssd_info;
ssize_t ret = 0;
int chp;
+ int mask;
- for (chp = 0; chp < 8; chp++)
- ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]);
+ for (chp = 0; chp < 8; chp++) {
+ mask = 0x80 >> chp;
+ if (ssd->path_mask & mask)
+ ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id);
+ else
+ ret += sprintf(buf + ret, "00 ");
+ }
ret += sprintf (buf+ret, "\n");
return min((ssize_t)PAGE_SIZE, ret);
}
@@ -280,7 +272,7 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf)
struct ccw_device_id *id = &(cdev->id);
int len;
- len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1;
+ len = snprint_alias(buf, PAGE_SIZE, id, "\n") + 1;
return len > PAGE_SIZE ? PAGE_SIZE : len;
}
@@ -298,16 +290,10 @@ int ccw_device_is_orphan(struct ccw_device *cdev)
return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));
}
-static void ccw_device_unregister(struct work_struct *work)
+static void ccw_device_unregister(struct ccw_device *cdev)
{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
-
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
if (test_and_clear_bit(1, &cdev->private->registered))
- device_unregister(&cdev->dev);
- put_device(&cdev->dev);
+ device_del(&cdev->dev);
}
static void
@@ -324,11 +310,8 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
spin_lock_irqsave(cdev->ccwlock, flags);
cdev->private->state = DEV_STATE_NOT_OPER;
spin_unlock_irqrestore(cdev->ccwlock, flags);
- if (get_device(&cdev->dev)) {
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_unregister);
- queue_work(ccw_device_work, &cdev->private->kick_work);
- }
+ ccw_device_unregister(cdev);
+ put_device(&cdev->dev);
return ;
}
sch = to_subchannel(cdev->dev.parent);
@@ -413,11 +396,60 @@ ccw_device_set_online(struct ccw_device *cdev)
return (ret == 0) ? -ENODEV : ret;
}
-static ssize_t
-online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static void online_store_handle_offline(struct ccw_device *cdev)
+{
+ if (cdev->private->state == DEV_STATE_DISCONNECTED)
+ ccw_device_remove_disconnected(cdev);
+ else if (cdev->drv && cdev->drv->set_offline)
+ ccw_device_set_offline(cdev);
+}
+
+static int online_store_recog_and_online(struct ccw_device *cdev)
+{
+ int ret;
+
+ /* Do device recognition, if needed. */
+ if (cdev->id.cu_type == 0) {
+ ret = ccw_device_recognition(cdev);
+ if (ret) {
+ printk(KERN_WARNING"Couldn't start recognition "
+ "for device %s (ret=%d)\n",
+ cdev->dev.bus_id, ret);
+ return ret;
+ }
+ wait_event(cdev->private->wait_q,
+ cdev->private->flags.recog_done);
+ }
+ if (cdev->drv && cdev->drv->set_online)
+ ccw_device_set_online(cdev);
+ return 0;
+}
+static void online_store_handle_online(struct ccw_device *cdev, int force)
+{
+ int ret;
+
+ ret = online_store_recog_and_online(cdev);
+ if (ret)
+ return;
+ if (force && cdev->private->state == DEV_STATE_BOXED) {
+ ret = ccw_device_stlck(cdev);
+ if (ret) {
+ printk(KERN_WARNING"ccw_device_stlck for device %s "
+ "returned %d!\n", cdev->dev.bus_id, ret);
+ return;
+ }
+ if (cdev->id.cu_type == 0)
+ cdev->private->state = DEV_STATE_NOT_OPER;
+ online_store_recog_and_online(cdev);
+ }
+
+}
+
+static ssize_t online_store (struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct ccw_device *cdev = to_ccwdev(dev);
- int i, force, ret;
+ int i, force;
char *tmp;
if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
@@ -434,51 +466,17 @@ online_store (struct device *dev, struct device_attribute *attr, const char *buf
force = 0;
i = simple_strtoul(buf, &tmp, 16);
}
- if (i == 1) {
- /* Do device recognition, if needed. */
- if (cdev->id.cu_type == 0) {
- ret = ccw_device_recognition(cdev);
- if (ret) {
- printk(KERN_WARNING"Couldn't start recognition "
- "for device %s (ret=%d)\n",
- cdev->dev.bus_id, ret);
- goto out;
- }
- wait_event(cdev->private->wait_q,
- cdev->private->flags.recog_done);
- }
- if (cdev->drv && cdev->drv->set_online)
- ccw_device_set_online(cdev);
- } else if (i == 0) {
- if (cdev->private->state == DEV_STATE_DISCONNECTED)
- ccw_device_remove_disconnected(cdev);
- else if (cdev->drv && cdev->drv->set_offline)
- ccw_device_set_offline(cdev);
- }
- if (force && cdev->private->state == DEV_STATE_BOXED) {
- ret = ccw_device_stlck(cdev);
- if (ret) {
- printk(KERN_WARNING"ccw_device_stlck for device %s "
- "returned %d!\n", cdev->dev.bus_id, ret);
- goto out;
- }
- /* Do device recognition, if needed. */
- if (cdev->id.cu_type == 0) {
- cdev->private->state = DEV_STATE_NOT_OPER;
- ret = ccw_device_recognition(cdev);
- if (ret) {
- printk(KERN_WARNING"Couldn't start recognition "
- "for device %s (ret=%d)\n",
- cdev->dev.bus_id, ret);
- goto out;
- }
- wait_event(cdev->private->wait_q,
- cdev->private->flags.recog_done);
- }
- if (cdev->drv && cdev->drv->set_online)
- ccw_device_set_online(cdev);
+
+ switch (i) {
+ case 0:
+ online_store_handle_offline(cdev);
+ break;
+ case 1:
+ online_store_handle_online(cdev, force);
+ break;
+ default:
+ count = -EINVAL;
}
- out:
if (cdev->drv)
module_put(cdev->drv->owner);
atomic_set(&cdev->private->onoff, 0);
@@ -548,17 +546,10 @@ static struct attribute_group ccwdev_attr_group = {
.attrs = ccwdev_attrs,
};
-static int
-device_add_files (struct device *dev)
-{
- return sysfs_create_group(&dev->kobj, &ccwdev_attr_group);
-}
-
-static void
-device_remove_files(struct device *dev)
-{
- sysfs_remove_group(&dev->kobj, &ccwdev_attr_group);
-}
+struct attribute_group *ccwdev_attr_groups[] = {
+ &ccwdev_attr_group,
+ NULL,
+};
/* this is a simple abstraction for device_register that sets the
* correct bus type and adds the bus specific files */
@@ -573,10 +564,6 @@ static int ccw_device_register(struct ccw_device *cdev)
return ret;
set_bit(1, &cdev->private->registered);
- if ((ret = device_add_files(dev))) {
- if (test_and_clear_bit(1, &cdev->private->registered))
- device_del(dev);
- }
return ret;
}
@@ -648,10 +635,6 @@ ccw_device_add_changed(struct work_struct *work)
return;
}
set_bit(1, &cdev->private->registered);
- if (device_add_files(&cdev->dev)) {
- if (test_and_clear_bit(1, &cdev->private->registered))
- device_unregister(&cdev->dev);
- }
}
void ccw_device_do_unreg_rereg(struct work_struct *work)
@@ -664,9 +647,7 @@ void ccw_device_do_unreg_rereg(struct work_struct *work)
cdev = priv->cdev;
sch = to_subchannel(cdev->dev.parent);
- device_remove_files(&cdev->dev);
- if (test_and_clear_bit(1, &cdev->private->registered))
- device_del(&cdev->dev);
+ ccw_device_unregister(cdev);
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_add_changed);
queue_work(ccw_device_work, &cdev->private->kick_work);
@@ -705,6 +686,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
cdev->dev.parent = &sch->dev;
cdev->dev.release = ccw_device_release;
INIT_LIST_HEAD(&cdev->private->kick_work.entry);
+ cdev->dev.groups = ccwdev_attr_groups;
/* Do first half of device_register. */
device_initialize(&cdev->dev);
if (!get_device(&sch->dev)) {
@@ -736,6 +718,7 @@ static int io_subchannel_recog(struct ccw_device *, struct subchannel *);
static void sch_attach_device(struct subchannel *sch,
struct ccw_device *cdev)
{
+ css_update_ssd_info(sch);
spin_lock_irq(sch->lock);
sch->dev.driver_data = cdev;
cdev->private->schid = sch->schid;
@@ -871,7 +854,7 @@ io_subchannel_register(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
sch = to_subchannel(cdev->dev.parent);
-
+ css_update_ssd_info(sch);
/*
* io_subchannel_register() will also be called after device
* recognition has been done for a boxed device (which will already
@@ -888,6 +871,12 @@ io_subchannel_register(struct work_struct *work)
}
goto out;
}
+ /*
+ * Now we know this subchannel will stay, we can throw
+ * our delayed uevent.
+ */
+ sch->dev.uevent_suppress = 0;
+ kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
/* make it known to the system */
ret = ccw_device_register(cdev);
if (ret) {
@@ -1133,15 +1122,8 @@ io_subchannel_remove (struct subchannel *sch)
sch->dev.driver_data = NULL;
cdev->private->state = DEV_STATE_NOT_OPER;
spin_unlock_irqrestore(cdev->ccwlock, flags);
- /*
- * Put unregistration on workqueue to avoid livelocks on the css bus
- * semaphore.
- */
- if (get_device(&cdev->dev)) {
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_unregister);
- queue_work(ccw_device_work, &cdev->private->kick_work);
- }
+ ccw_device_unregister(cdev);
+ put_device(&cdev->dev);
return 0;
}
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 089a3ddd6265..898ec3b2bebb 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -15,6 +15,7 @@
#include <asm/ccwdev.h>
#include <asm/cio.h>
+#include <asm/chpid.h>
#include "cio.h"
#include "cio_debug.h"
@@ -22,6 +23,7 @@
#include "device.h"
#include "chsc.h"
#include "ioasm.h"
+#include "chp.h"
int
device_is_online(struct subchannel *sch)
@@ -210,14 +212,18 @@ static void
__recover_lost_chpids(struct subchannel *sch, int old_lpm)
{
int mask, i;
+ struct chp_id chpid;
+ chp_id_init(&chpid);
for (i = 0; i<8; i++) {
mask = 0x80 >> i;
if (!(sch->lpm & mask))
continue;
if (old_lpm & mask)
continue;
- chpid_is_actually_online(sch->schib.pmcw.chpid[i]);
+ chpid.id = sch->schib.pmcw.chpid[i];
+ if (!chp_is_registered(chpid))
+ css_schedule_eval_all();
}
}
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 7c7775aae38a..16f59fcb66b1 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -16,12 +16,14 @@
#include <asm/ccwdev.h>
#include <asm/idals.h>
+#include <asm/chpid.h>
#include "cio.h"
#include "cio_debug.h"
#include "css.h"
#include "chsc.h"
#include "device.h"
+#include "chp.h"
int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags)
{
@@ -606,9 +608,12 @@ void *
ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no)
{
struct subchannel *sch;
+ struct chp_id chpid;
sch = to_subchannel(cdev->dev.parent);
- return chsc_get_chp_desc(sch, chp_no);
+ chp_id_init(&chpid);
+ chpid.id = sch->schib.pmcw.chpid[chp_no];
+ return chp_get_chp_desc(chpid);
}
// FIXME: these have to go:
diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c
new file mode 100644
index 000000000000..16ea828e99f7
--- /dev/null
+++ b/drivers/s390/cio/idset.c
@@ -0,0 +1,112 @@
+/*
+ * drivers/s390/cio/idset.c
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/slab.h>
+#include <asm/bitops.h>
+#include "idset.h"
+#include "css.h"
+
+struct idset {
+ int num_ssid;
+ int num_id;
+ unsigned long bitmap[0];
+};
+
+static inline unsigned long bitmap_size(int num_ssid, int num_id)
+{
+ return __BITOPS_WORDS(num_ssid * num_id) * sizeof(unsigned long);
+}
+
+static struct idset *idset_new(int num_ssid, int num_id)
+{
+ struct idset *set;
+
+ set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id),
+ GFP_KERNEL);
+ if (set) {
+ set->num_ssid = num_ssid;
+ set->num_id = num_id;
+ }
+ return set;
+}
+
+void idset_free(struct idset *set)
+{
+ kfree(set);
+}
+
+void idset_clear(struct idset *set)
+{
+ memset(set->bitmap, 0, bitmap_size(set->num_ssid, set->num_id));
+}
+
+void idset_fill(struct idset *set)
+{
+ memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id));
+}
+
+static inline void idset_add(struct idset *set, int ssid, int id)
+{
+ set_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline void idset_del(struct idset *set, int ssid, int id)
+{
+ clear_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline int idset_contains(struct idset *set, int ssid, int id)
+{
+ return test_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline int idset_get_first(struct idset *set, int *ssid, int *id)
+{
+ int bitnum;
+
+ bitnum = find_first_bit(set->bitmap, set->num_ssid * set->num_id);
+ if (bitnum >= set->num_ssid * set->num_id)
+ return 0;
+ *ssid = bitnum / set->num_id;
+ *id = bitnum % set->num_id;
+ return 1;
+}
+
+struct idset *idset_sch_new(void)
+{
+ return idset_new(__MAX_SSID + 1, __MAX_SUBCHANNEL + 1);
+}
+
+void idset_sch_add(struct idset *set, struct subchannel_id schid)
+{
+ idset_add(set, schid.ssid, schid.sch_no);
+}
+
+void idset_sch_del(struct idset *set, struct subchannel_id schid)
+{
+ idset_del(set, schid.ssid, schid.sch_no);
+}
+
+int idset_sch_contains(struct idset *set, struct subchannel_id schid)
+{
+ return idset_contains(set, schid.ssid, schid.sch_no);
+}
+
+int idset_sch_get_first(struct idset *set, struct subchannel_id *schid)
+{
+ int ssid = 0;
+ int id = 0;
+ int rc;
+
+ rc = idset_get_first(set, &ssid, &id);
+ if (rc) {
+ init_subchannel_id(schid);
+ schid->ssid = ssid;
+ schid->sch_no = id;
+ }
+ return rc;
+}
diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h
new file mode 100644
index 000000000000..144466ab8c15
--- /dev/null
+++ b/drivers/s390/cio/idset.h
@@ -0,0 +1,25 @@
+/*
+ * drivers/s390/cio/idset.h
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef S390_IDSET_H
+#define S390_IDSET_H S390_IDSET_H
+
+#include "schid.h"
+
+struct idset;
+
+void idset_free(struct idset *set);
+void idset_clear(struct idset *set);
+void idset_fill(struct idset *set);
+
+struct idset *idset_sch_new(void);
+void idset_sch_add(struct idset *set, struct subchannel_id id);
+void idset_sch_del(struct idset *set, struct subchannel_id id);
+int idset_sch_contains(struct idset *set, struct subchannel_id id);
+int idset_sch_get_first(struct idset *set, struct subchannel_id *id);
+
+#endif /* S390_IDSET_H */
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index ad6d82940069..7153dd959082 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -1,6 +1,7 @@
#ifndef S390_CIO_IOASM_H
#define S390_CIO_IOASM_H
+#include <asm/chpid.h>
#include "schid.h"
/*
@@ -189,9 +190,9 @@ static inline int chsc(void *chsc_area)
return cc;
}
-static inline int rchp(int chpid)
+static inline int rchp(struct chp_id chpid)
{
- register unsigned int reg1 asm ("1") = chpid;
+ register struct chp_id reg1 asm ("1") = chpid;
int ccode;
asm volatile(
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index bf37cdf43fae..5aac0ec36368 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -423,27 +423,25 @@ static int ap_uevent (struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct ap_device *ap_dev = to_ap_dev(dev);
- int length;
+ int retval = 0, length = 0, i = 0;
if (!ap_dev)
return -ENODEV;
/* Set up DEV_TYPE environment variable. */
- envp[0] = buffer;
- length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X",
- ap_dev->device_type);
- if (buffer_size - length <= 0)
- return -ENOMEM;
- buffer += length;
- buffer_size -= length;
+ retval = add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "DEV_TYPE=%04X", ap_dev->device_type);
+ if (retval)
+ return retval;
+
/* Add MODALIAS= */
- envp[1] = buffer;
- length = scnprintf(buffer, buffer_size, "MODALIAS=ap:t%02X",
- ap_dev->device_type);
- if (buffer_size - length <= 0)
- return -ENOMEM;
- envp[2] = NULL;
- return 0;
+ retval = add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MODALIAS=ap:t%02X", ap_dev->device_type);
+
+ envp[i] = NULL;
+ return retval;
}
static struct bus_type ap_bus_type = {
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 7809a79feec7..6dd64d0c8d45 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -3525,8 +3525,8 @@ unpack_next:
memcpy(skb_put(skb,len_of_data),
privptr->p_mtc_envelope,
len_of_data);
- skb->mac.raw=skb->data;
skb->dev=dev;
+ skb_reset_mac_header(skb);
skb->protocol=htons(ETH_P_IP);
skb->ip_summed=CHECKSUM_UNNECESSARY;
privptr->stats.rx_packets++;
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
index 0d6d5fcc128b..b20fd0681733 100644
--- a/drivers/s390/net/ctcmain.c
+++ b/drivers/s390/net/ctcmain.c
@@ -455,7 +455,7 @@ ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
return;
}
skb_put(pskb, header->length);
- pskb->mac.raw = pskb->data;
+ skb_reset_mac_header(pskb);
len -= header->length;
skb = dev_alloc_skb(pskb->len);
if (!skb) {
@@ -472,8 +472,9 @@ ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
privptr->stats.rx_dropped++;
return;
}
- memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len);
- skb->mac.raw = skb->data;
+ skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
+ pskb->len);
+ skb_reset_mac_header(skb);
skb->dev = pskb->dev;
skb->protocol = pskb->protocol;
pskb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -706,7 +707,8 @@ ch_action_txdone(fsm_instance * fi, int event, void *arg)
spin_unlock(&ch->collect_lock);
return;
}
- ch->trans_skb->tail = ch->trans_skb->data = ch->trans_skb_data;
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
ch->trans_skb->len = 0;
if (ch->prof.maxmulti < (ch->collect_len + 2))
ch->prof.maxmulti = ch->collect_len + 2;
@@ -715,8 +717,9 @@ ch_action_txdone(fsm_instance * fi, int event, void *arg)
*((__u16 *) skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
i = 0;
while ((skb = skb_dequeue(&ch->collect_queue))) {
- memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
- skb->len);
+ skb_copy_from_linear_data(skb, skb_put(ch->trans_skb,
+ skb->len),
+ skb->len);
privptr->stats.tx_packets++;
privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
atomic_dec(&skb->users);
@@ -831,7 +834,8 @@ ch_action_rx(fsm_instance * fi, int event, void *arg)
ctc_unpack_skb(ch, skb);
}
again:
- skb->data = skb->tail = ch->trans_skb_data;
+ skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(skb);
skb->len = 0;
if (ctc_checkalloc_buffer(ch, 1))
return;
@@ -1638,21 +1642,19 @@ add_channel(struct ccw_device *cdev, enum channel_types type)
struct channel *ch;
DBF_TEXT(trace, 2, __FUNCTION__);
- if ((ch =
- (struct channel *) kmalloc(sizeof (struct channel),
- GFP_KERNEL)) == NULL) {
+ ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
+ if (!ch) {
ctc_pr_warn("ctc: Out of memory in add_channel\n");
return -1;
}
- memset(ch, 0, sizeof (struct channel));
- if ((ch->ccw = kmalloc(8*sizeof(struct ccw1),
- GFP_KERNEL | GFP_DMA)) == NULL) {
+ /* assure all flags and counters are reset */
+ ch->ccw = kzalloc(8 * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
+ if (!ch->ccw) {
kfree(ch);
ctc_pr_warn("ctc: Out of memory in add_channel\n");
return -1;
}
- memset(ch->ccw, 0, 8*sizeof(struct ccw1)); // assure all flags and counters are reset
/**
* "static" ccws are used in the following way:
@@ -1692,15 +1694,14 @@ add_channel(struct ccw_device *cdev, enum channel_types type)
return -1;
}
fsm_newstate(ch->fsm, CH_STATE_IDLE);
- if ((ch->irb = kmalloc(sizeof (struct irb),
- GFP_KERNEL)) == NULL) {
+ ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
+ if (!ch->irb) {
ctc_pr_warn("ctc: Out of memory in add_channel\n");
kfree_fsm(ch->fsm);
kfree(ch->ccw);
kfree(ch);
return -1;
}
- memset(ch->irb, 0, sizeof (struct irb));
while (*c && less_than((*c)->id, ch->id))
c = &(*c)->next;
if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) {
@@ -2226,7 +2227,8 @@ transmit_skb(struct channel *ch, struct sk_buff *skb)
* IDAL support in CTC is broken, so we have to
* care about skb's above 2G ourselves.
*/
- hi = ((unsigned long) skb->tail + LL_HEADER_LENGTH) >> 31;
+ hi = ((unsigned long)skb_tail_pointer(skb) +
+ LL_HEADER_LENGTH) >> 31;
if (hi) {
nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
if (!nskb) {
@@ -2262,11 +2264,12 @@ transmit_skb(struct channel *ch, struct sk_buff *skb)
return -EBUSY;
}
- ch->trans_skb->tail = ch->trans_skb->data;
+ skb_reset_tail_pointer(ch->trans_skb);
ch->trans_skb->len = 0;
ch->ccw[1].count = skb->len;
- memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
- skb->len);
+ skb_copy_from_linear_data(skb, skb_put(ch->trans_skb,
+ skb->len),
+ skb->len);
atomic_dec(&skb->users);
dev_kfree_skb_irq(skb);
ccw_idx = 0;
@@ -2745,14 +2748,13 @@ ctc_probe_device(struct ccwgroup_device *cgdev)
if (!get_device(&cgdev->dev))
return -ENODEV;
- priv = kmalloc(sizeof (struct ctc_priv), GFP_KERNEL);
+ priv = kzalloc(sizeof(struct ctc_priv), GFP_KERNEL);
if (!priv) {
ctc_pr_err("%s: Out of memory\n", __func__);
put_device(&cgdev->dev);
return -ENOMEM;
}
- memset(priv, 0, sizeof (struct ctc_priv));
rc = ctc_add_files(&cgdev->dev);
if (rc) {
kfree(priv);
@@ -2793,10 +2795,9 @@ ctc_init_netdevice(struct net_device * dev, int alloc_device,
DBF_TEXT(setup, 3, __FUNCTION__);
if (alloc_device) {
- dev = kmalloc(sizeof (struct net_device), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct net_device), GFP_KERNEL);
if (!dev)
return NULL;
- memset(dev, 0, sizeof (struct net_device));
}
dev->priv = privptr;
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index ecca1046714e..08a994fdd1a4 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1576,7 +1576,7 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb,
header->offset = card->tx_buffer->count;
header->type = card->lan_type;
header->slot = card->portno;
- memcpy(header + 1, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, header + 1, skb->len);
spin_unlock(&card->lock);
card->stats.tx_bytes += skb->len;
card->stats.tx_packets++;
@@ -1784,7 +1784,6 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
card->stats.rx_dropped++;
return;
}
- skb->dev = card->dev;
memcpy(skb_put(skb, skb_len), skb_data, skb_len);
skb->protocol = card->lan_type_trans(skb, card->dev);
card->stats.rx_bytes += skb_len;
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 594320ca1b7c..e10e85e85c84 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -635,7 +635,7 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
return;
}
skb_put(pskb, header->next);
- pskb->mac.raw = pskb->data;
+ skb_reset_mac_header(pskb);
skb = dev_alloc_skb(pskb->len);
if (!skb) {
PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
@@ -645,8 +645,9 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
privptr->stats.rx_dropped++;
return;
}
- memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len);
- skb->mac.raw = skb->data;
+ skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
+ pskb->len);
+ skb_reset_mac_header(skb);
skb->dev = pskb->dev;
skb->protocol = pskb->protocol;
pskb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -689,7 +690,8 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg)
msg->length, conn->max_buffsize);
return;
}
- conn->rx_buff->data = conn->rx_buff->tail = conn->rx_buff->head;
+ conn->rx_buff->data = conn->rx_buff->head;
+ skb_reset_tail_pointer(conn->rx_buff);
conn->rx_buff->len = 0;
rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data,
msg->length, NULL);
@@ -735,14 +737,17 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
}
}
}
- conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
+ conn->tx_buff->data = conn->tx_buff->head;
+ skb_reset_tail_pointer(conn->tx_buff);
conn->tx_buff->len = 0;
spin_lock_irqsave(&conn->collect_lock, saveflags);
while ((skb = skb_dequeue(&conn->collect_queue))) {
header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
NETIUCV_HDRLEN);
- memcpy(skb_put(conn->tx_buff, skb->len), skb->data, skb->len);
+ skb_copy_from_linear_data(skb,
+ skb_put(conn->tx_buff, skb->len),
+ skb->len);
txbytes += skb->len;
txpackets++;
stat_maxcq++;
@@ -1164,8 +1169,8 @@ static int netiucv_transmit_skb(struct iucv_connection *conn,
* Copy the skb to a new allocated skb in lowmem only if the
* data is located above 2G in memory or tailroom is < 2.
*/
- unsigned long hi =
- ((unsigned long)(skb->tail + NETIUCV_HDRLEN)) >> 31;
+ unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) +
+ NETIUCV_HDRLEN)) >> 31;
int copied = 0;
if (hi || (skb_tailroom(skb) < 2)) {
nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c
index 7c735e1fe063..dd7034fbfff8 100644
--- a/drivers/s390/net/qeth_eddp.c
+++ b/drivers/s390/net/qeth_eddp.c
@@ -267,7 +267,8 @@ qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len,
QETH_DBF_TEXT(trace, 5, "eddpcdtc");
if (skb_shinfo(eddp->skb)->nr_frags == 0) {
- memcpy(dst, eddp->skb->data + eddp->skb_offset, len);
+ skb_copy_from_linear_data_offset(eddp->skb, eddp->skb_offset,
+ dst, len);
*hcsum = csum_partial(eddp->skb->data + eddp->skb_offset, len,
*hcsum);
eddp->skb_offset += len;
@@ -416,7 +417,7 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
eddp->skb_offset += VLAN_HLEN;
#endif /* CONFIG_QETH_VLAN */
}
- tcph = eddp->skb->h.th;
+ tcph = tcp_hdr(eddp->skb);
while (eddp->skb_offset < eddp->skb->len) {
data_len = min((int)skb_shinfo(eddp->skb)->gso_size,
(int)(eddp->skb->len - eddp->skb_offset));
@@ -473,20 +474,24 @@ qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
QETH_DBF_TEXT(trace, 5, "eddpficx");
/* create our segmentation headers and copy original headers */
if (skb->protocol == htons(ETH_P_IP))
- eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.iph,
- skb->nh.iph->ihl*4,
- (u8 *)skb->h.th, skb->h.th->doff*4);
+ eddp = qeth_eddp_create_eddp_data(qhdr,
+ skb_network_header(skb),
+ ip_hdrlen(skb),
+ skb_transport_header(skb),
+ tcp_hdrlen(skb));
else
- eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.ipv6h,
- sizeof(struct ipv6hdr),
- (u8 *)skb->h.th, skb->h.th->doff*4);
+ eddp = qeth_eddp_create_eddp_data(qhdr,
+ skb_network_header(skb),
+ sizeof(struct ipv6hdr),
+ skb_transport_header(skb),
+ tcp_hdrlen(skb));
if (eddp == NULL) {
QETH_DBF_TEXT(trace, 2, "eddpfcnm");
return -ENOMEM;
}
if (qhdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) {
- skb->mac.raw = (skb->data) + sizeof(struct qeth_hdr);
+ skb_set_mac_header(skb, sizeof(struct qeth_hdr));
memcpy(&eddp->mac, eth_hdr(skb), ETH_HLEN);
#ifdef CONFIG_QETH_VLAN
if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) {
@@ -590,12 +595,13 @@ qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb,
QETH_DBF_TEXT(trace, 5, "creddpct");
if (skb->protocol == htons(ETH_P_IP))
ctx = qeth_eddp_create_context_generic(card, skb,
- sizeof(struct qeth_hdr) + skb->nh.iph->ihl*4 +
- skb->h.th->doff*4);
+ (sizeof(struct qeth_hdr) +
+ ip_hdrlen(skb) +
+ tcp_hdrlen(skb)));
else if (skb->protocol == htons(ETH_P_IPV6))
ctx = qeth_eddp_create_context_generic(card, skb,
sizeof(struct qeth_hdr) + sizeof(struct ipv6hdr) +
- skb->h.th->doff*4);
+ tcp_hdrlen(skb));
else
QETH_DBF_TEXT(trace, 2, "cetcpinv");
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
index d8a86f5af379..ad7792dc1a04 100644
--- a/drivers/s390/net/qeth_main.c
+++ b/drivers/s390/net/qeth_main.c
@@ -2278,7 +2278,7 @@ qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
(card->info.link_type == QETH_LINK_TYPE_LANE_TR))
return tr_type_trans(skb,dev);
#endif /* CONFIG_TR */
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN );
eth = eth_hdr(skb);
@@ -2306,9 +2306,9 @@ qeth_rebuild_skb_fake_ll_tr(struct qeth_card *card, struct sk_buff *skb,
struct iphdr *ip_hdr;
QETH_DBF_TEXT(trace,5,"skbfktr");
- skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_TR;
+ skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_TR);
/* this is a fake ethernet header */
- fake_hdr = (struct trh_hdr *) skb->mac.raw;
+ fake_hdr = tr_hdr(skb);
/* the destination MAC address */
switch (skb->pkt_type){
@@ -2359,9 +2359,9 @@ qeth_rebuild_skb_fake_ll_eth(struct qeth_card *card, struct sk_buff *skb,
struct iphdr *ip_hdr;
QETH_DBF_TEXT(trace,5,"skbfketh");
- skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_ETH;
+ skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_ETH);
/* this is a fake ethernet header */
- fake_hdr = (struct ethhdr *) skb->mac.raw;
+ fake_hdr = eth_hdr(skb);
/* the destination MAC address */
switch (skb->pkt_type){
@@ -2461,7 +2461,7 @@ qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
if (card->options.fake_ll)
qeth_rebuild_skb_fake_ll(card, skb, hdr);
else
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb->ip_summed = card->options.checksum_type;
if (card->options.checksum_type == HW_CHECKSUMMING){
if ( (hdr->hdr.l3.ext_flags &
@@ -2501,7 +2501,8 @@ qeth_process_inbound_buffer(struct qeth_card *card,
vlan_tag = qeth_rebuild_skb(card, skb, hdr);
else { /*in case of OSN*/
skb_push(skb, sizeof(struct qeth_hdr));
- memcpy(skb->data, hdr, sizeof(struct qeth_hdr));
+ skb_copy_to_linear_data(skb, hdr,
+ sizeof(struct qeth_hdr));
}
/* is device UP ? */
if (!(card->dev->flags & IFF_UP)){
@@ -3778,9 +3779,11 @@ qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
}
/* try something else */
if (skb->protocol == ETH_P_IPV6)
- return (skb->nh.raw[24] == 0xff) ? RTN_MULTICAST : 0;
+ return (skb_network_header(skb)[24] == 0xff) ?
+ RTN_MULTICAST : 0;
else if (skb->protocol == ETH_P_IP)
- return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0;
+ return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ?
+ RTN_MULTICAST : 0;
/* ... */
if (!memcmp(skb->data, skb->dev->broadcast, 6))
return RTN_BROADCAST;
@@ -3818,18 +3821,20 @@ qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
return card->info.is_multicast_different &
(card->qdio.no_out_queues - 1);
if (card->qdio.do_prio_queueing && (ipv == 4)) {
+ const u8 tos = ip_hdr(skb)->tos;
+
if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_TOS){
- if (skb->nh.iph->tos & IP_TOS_NOTIMPORTANT)
+ if (tos & IP_TOS_NOTIMPORTANT)
return 3;
- if (skb->nh.iph->tos & IP_TOS_HIGHRELIABILITY)
+ if (tos & IP_TOS_HIGHRELIABILITY)
return 2;
- if (skb->nh.iph->tos & IP_TOS_HIGHTHROUGHPUT)
+ if (tos & IP_TOS_HIGHTHROUGHPUT)
return 1;
- if (skb->nh.iph->tos & IP_TOS_LOWDELAY)
+ if (tos & IP_TOS_LOWDELAY)
return 0;
}
if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_PREC)
- return 3 - (skb->nh.iph->tos >> 6);
+ return 3 - (tos >> 6);
} else if (card->qdio.do_prio_queueing && (ipv == 6)) {
/* TODO: IPv6!!! */
}
@@ -3866,9 +3871,9 @@ __qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb, int ipv)
* memcpys instead of one memmove to save cycles.
*/
skb_push(skb, VLAN_HLEN);
- memcpy(skb->data, skb->data + 4, 4);
- memcpy(skb->data + 4, skb->data + 8, 4);
- memcpy(skb->data + 8, skb->data + 12, 4);
+ skb_copy_to_linear_data(skb, skb->data + 4, 4);
+ skb_copy_to_linear_data_offset(skb, 4, skb->data + 8, 4);
+ skb_copy_to_linear_data_offset(skb, 8, skb->data + 12, 4);
tag = (u16 *)(skb->data + 12);
/*
* first two bytes = ETH_P_8021Q (0x8100)
@@ -4039,7 +4044,8 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
*((u32 *) skb->dst->neighbour->primary_key);
} else {
/* fill in destination address used in ip header */
- *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = skb->nh.iph->daddr;
+ *((u32 *)(&hdr->hdr.l3.dest_addr[12])) =
+ ip_hdr(skb)->daddr;
}
} else if (ipv == 6) { /* IPv6 or passthru */
hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags6(cast_type);
@@ -4048,7 +4054,8 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
skb->dst->neighbour->primary_key, 16);
} else {
/* fill in destination address used in ip header */
- memcpy(hdr->hdr.l3.dest_addr, &skb->nh.ipv6h->daddr, 16);
+ memcpy(hdr->hdr.l3.dest_addr,
+ &ipv6_hdr(skb)->daddr, 16);
}
} else { /* passthrough */
if((skb->dev->type == ARPHRD_IEEE802_TR) &&
diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c
index 81f805cc5ee7..89d56c8ecdd2 100644
--- a/drivers/s390/net/qeth_proc.c
+++ b/drivers/s390/net/qeth_proc.c
@@ -37,7 +37,6 @@ qeth_procfile_seq_start(struct seq_file *s, loff_t *offset)
struct device *dev = NULL;
loff_t nr = 0;
- down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
if (*offset == 0)
return SEQ_START_TOKEN;
while (1) {
@@ -53,7 +52,6 @@ qeth_procfile_seq_start(struct seq_file *s, loff_t *offset)
static void
qeth_procfile_seq_stop(struct seq_file *s, void* it)
{
- up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
}
static void *
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h
index 14504afb044e..c20e923cf9ad 100644
--- a/drivers/s390/net/qeth_tso.h
+++ b/drivers/s390/net/qeth_tso.h
@@ -40,8 +40,8 @@ qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb)
QETH_DBF_TEXT(trace, 5, "tsofhdr");
hdr = (struct qeth_hdr_tso *) skb->data;
- iph = skb->nh.iph;
- tcph = skb->h.th;
+ iph = ip_hdr(skb);
+ tcph = tcp_hdr(skb);
/*fix header to TSO values ...*/
hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
/*set values which are fix for the first approach ...*/
@@ -63,13 +63,9 @@ qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb)
static inline void
qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb)
{
- struct iphdr *iph;
- struct ipv6hdr *ip6h;
- struct tcphdr *tcph;
-
- iph = skb->nh.iph;
- ip6h = skb->nh.ipv6h;
- tcph = skb->h.th;
+ struct iphdr *iph = ip_hdr(skb);
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ struct tcphdr *tcph = tcp_hdr(skb);
tcph->check = 0;
if (skb->protocol == ETH_P_IPV6) {
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index 806bb1a921eb..644a06eba828 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -21,6 +21,7 @@
#include "cio/cio.h"
#include "cio/chsc.h"
#include "cio/css.h"
+#include "cio/chp.h"
#include "s390mach.h"
static struct semaphore m_sem;
@@ -44,14 +45,13 @@ static int
s390_collect_crw_info(void *param)
{
struct crw crw[2];
- int ccode, ret, slow;
+ int ccode;
struct semaphore *sem;
unsigned int chain;
sem = (struct semaphore *)param;
repeat:
down_interruptible(sem);
- slow = 0;
chain = 0;
while (1) {
if (unlikely(chain > 1)) {
@@ -84,9 +84,8 @@ repeat:
/* Check for overflows. */
if (crw[chain].oflw) {
pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
- css_reiterate_subchannels();
+ css_schedule_eval_all();
chain = 0;
- slow = 1;
continue;
}
switch (crw[chain].rsc) {
@@ -94,10 +93,7 @@ repeat:
if (crw[0].chn && !chain)
break;
pr_debug("source is subchannel %04X\n", crw[0].rsid);
- ret = css_process_crw (crw[0].rsid,
- chain ? crw[1].rsid : 0);
- if (ret == -EAGAIN)
- slow = 1;
+ css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0);
break;
case CRW_RSC_MONITOR:
pr_debug("source is monitoring facility\n");
@@ -116,28 +112,23 @@ repeat:
}
switch (crw[0].erc) {
case CRW_ERC_IPARM: /* Path has come. */
- ret = chp_process_crw(crw[0].rsid, 1);
+ chp_process_crw(crw[0].rsid, 1);
break;
case CRW_ERC_PERRI: /* Path has gone. */
case CRW_ERC_PERRN:
- ret = chp_process_crw(crw[0].rsid, 0);
+ chp_process_crw(crw[0].rsid, 0);
break;
default:
pr_debug("Don't know how to handle erc=%x\n",
crw[0].erc);
- ret = 0;
}
- if (ret == -EAGAIN)
- slow = 1;
break;
case CRW_RSC_CONFIG:
pr_debug("source is configuration-alert facility\n");
break;
case CRW_RSC_CSS:
pr_debug("source is channel subsystem\n");
- ret = chsc_process_crw();
- if (ret == -EAGAIN)
- slow = 1;
+ chsc_process_crw();
break;
default:
pr_debug("unknown source\n");
@@ -146,8 +137,6 @@ repeat:
/* chain is always 0 or 1 here. */
chain = crw[chain].chn ? chain + 1 : 0;
}
- if (slow)
- queue_work(slow_path_wq, &slow_path_work);
goto repeat;
return 0;
}
diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c
index 090743d2f914..19343f9675c3 100644
--- a/drivers/s390/sysinfo.c
+++ b/drivers/s390/sysinfo.c
@@ -357,6 +357,24 @@ static __init int create_proc_sysinfo(void)
__initcall(create_proc_sysinfo);
+int get_cpu_capability(unsigned int *capability)
+{
+ struct sysinfo_1_2_2 *info;
+ int rc;
+
+ info = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ rc = stsi(info, 1, 2, 2);
+ if (rc == -ENOSYS)
+ goto out;
+ rc = 0;
+ *capability = info->capability;
+out:
+ free_page((unsigned long) info);
+ return rc;
+}
+
/*
* CPU capability might have changed. Therefore recalculate loops_per_jiffy.
*/
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c
index 2cea4f5d2084..f2be2ead8742 100644
--- a/drivers/sbus/char/envctrl.c
+++ b/drivers/sbus/char/envctrl.c
@@ -726,7 +726,7 @@ static struct miscdevice envctrl_dev = {
* Return: None.
*/
static void envctrl_set_mon(struct i2c_child_t *pchild,
- char *chnl_desc,
+ const char *chnl_desc,
int chnl_no)
{
/* Firmware only has temperature type. It does not distinguish
@@ -763,8 +763,8 @@ static void envctrl_set_mon(struct i2c_child_t *pchild,
static void envctrl_init_adc(struct i2c_child_t *pchild, struct device_node *dp)
{
int i = 0, len;
- char *pos;
- unsigned int *pval;
+ const char *pos;
+ const unsigned int *pval;
/* Firmware describe channels into a stream separated by a '\0'. */
pos = of_get_property(dp, "channels-description", &len);
@@ -859,7 +859,7 @@ static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child,
{
int len, i, tbls_size = 0;
struct device_node *dp = edev_child->prom_node;
- void *pval;
+ const void *pval;
/* Get device address. */
pval = of_get_property(dp, "reg", &len);
diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c
index 6e99507aeb12..262f01e68592 100644
--- a/drivers/sbus/char/flash.c
+++ b/drivers/sbus/char/flash.c
@@ -190,7 +190,7 @@ static int __init flash_init(void)
}
if (!sdev) {
#ifdef CONFIG_PCI
- struct linux_prom_registers *ebus_regs;
+ const struct linux_prom_registers *ebus_regs;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c
index eec28c142a59..fbfeb89a6f32 100644
--- a/drivers/sbus/char/openprom.c
+++ b/drivers/sbus/char/openprom.c
@@ -44,7 +44,6 @@
#include <asm/openpromio.h>
#ifdef CONFIG_PCI
#include <linux/pci.h>
-#include <asm/pbm.h>
#endif
MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be)");
@@ -141,7 +140,7 @@ static int copyout(void __user *info, struct openpromio *opp, int len)
static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
{
- void *pval;
+ const void *pval;
int len;
if (!dp ||
@@ -248,18 +247,18 @@ static int oprompci2node(void __user *argp, struct device_node *dp, struct openp
if (bufsize >= 2*sizeof(int)) {
#ifdef CONFIG_PCI
struct pci_dev *pdev;
- struct pcidev_cookie *pcp;
- pdev = pci_find_slot (((int *) op->oprom_array)[0],
+ struct device_node *dp;
+
+ pdev = pci_get_bus_and_slot (((int *) op->oprom_array)[0],
((int *) op->oprom_array)[1]);
- pcp = pdev->sysdata;
- if (pcp != NULL) {
- dp = pcp->prom_node;
- data->current_node = dp;
- *((int *)op->oprom_array) = dp->node;
- op->oprom_size = sizeof(int);
- err = copyout(argp, op, bufsize + sizeof(int));
- }
+ dp = pci_device_to_OF_node(pdev);
+ data->current_node = dp;
+ *((int *)op->oprom_array) = dp->node;
+ op->oprom_size = sizeof(int);
+ err = copyout(argp, op, bufsize + sizeof(int));
+
+ pci_dev_put(pdev);
#endif
}
@@ -409,7 +408,7 @@ static int opiocget(void __user *argp, DATA *data)
struct opiocdesc op;
struct device_node *dp;
char *str;
- void *pval;
+ const void *pval;
int err, len;
if (copy_from_user(&op, argp, sizeof(op)))
diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c
index 8bfb67ccdcd4..c3135e2fbd5a 100644
--- a/drivers/sbus/char/vfc_dev.c
+++ b/drivers/sbus/char/vfc_dev.c
@@ -259,11 +259,10 @@ static int vfc_debug(struct vfc_dev *dev, int cmd, void __user *argp)
if (copy_from_user(&inout, argp, sizeof(inout)))
return -EFAULT;
- buffer = kmalloc(inout.len, GFP_KERNEL);
+ buffer = kzalloc(inout.len, GFP_KERNEL);
if (buffer == NULL)
return -ENOMEM;
- memset(buffer,0,inout.len);
vfc_lock_device(dev);
inout.ret=
vfc_i2c_recvbuf(dev,inout.addr & 0xff
diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
index 6349dd617f85..eee590a51d8a 100644
--- a/drivers/sbus/sbus.c
+++ b/drivers/sbus/sbus.c
@@ -35,7 +35,7 @@ struct sbus_bus *sbus_root;
static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sdev)
{
unsigned long base;
- void *pval;
+ const void *pval;
int len, err;
sdev->prom_node = dp->node;
@@ -86,7 +86,7 @@ static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sde
static void __init sbus_bus_ranges_init(struct device_node *dp, struct sbus_bus *sbus)
{
- void *pval;
+ const void *pval;
int len;
pval = of_get_property(dp, "ranges", &len);
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
index bf5d63e1beee..656bdb1352d8 100644
--- a/drivers/scsi/3w-xxxx.c
+++ b/drivers/scsi/3w-xxxx.c
@@ -1864,10 +1864,17 @@ static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
/* This function will handle the request sense scsi command */
static int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
{
+ char request_buffer[18];
+
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
- /* For now we just zero the request buffer */
- memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+ memset(request_buffer, 0, sizeof(request_buffer));
+ request_buffer[0] = 0x70; /* Immediate fixed format */
+ request_buffer[7] = 10; /* minimum size per SPC: 18 bytes */
+ /* leave all other fields zero, giving effectively NO_SENSE return */
+ tw_transfer_internal(tw_dev, request_id, request_buffer,
+ sizeof(request_buffer));
+
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 4cd280e86966..fcc4cb6c7f46 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1763,9 +1763,15 @@ config SUN3X_ESP
The ESP was an on-board SCSI controller used on Sun 3/80
machines. Say Y here to compile in support for it.
+config SCSI_ESP_CORE
+ tristate "ESP Scsi Driver Core"
+ depends on SCSI
+ select SCSI_SPI_ATTRS
+
config SCSI_SUNESP
tristate "Sparc ESP Scsi Driver"
depends on SBUS && SCSI
+ select SCSI_ESP_CORE
help
This is the driver for the Sun ESP SCSI host adapter. The ESP
chipset is present in most SPARC SBUS-based computers.
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 79ecf4ebe6eb..70cff4c599d7 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -106,7 +106,8 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
obj-$(CONFIG_MEGARAID_SAS) += megaraid/
obj-$(CONFIG_SCSI_ACARD) += atp870u.o
-obj-$(CONFIG_SCSI_SUNESP) += esp.o
+obj-$(CONFIG_SCSI_ESP_CORE) += esp_scsi.o
+obj-$(CONFIG_SCSI_SUNESP) += sun_esp.o
obj-$(CONFIG_SCSI_GDTH) += gdth.o
obj-$(CONFIG_SCSI_INITIO) += initio.o
obj-$(CONFIG_SCSI_INIA100) += a100u2w.o
diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c
deleted file mode 100644
index 2c2fe80bc42a..000000000000
--- a/drivers/scsi/esp.c
+++ /dev/null
@@ -1,4394 +0,0 @@
-/* esp.c: ESP Sun SCSI driver.
- *
- * Copyright (C) 1995, 1998, 2006 David S. Miller (davem@davemloft.net)
- */
-
-/* TODO:
- *
- * 1) Maybe disable parity checking in config register one for SCSI1
- * targets. (Gilmore says parity error on the SBus can lock up
- * old sun4c's)
- * 2) Add support for DMA2 pipelining.
- * 3) Add tagged queueing.
- */
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/blkdev.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-
-#include "esp.h"
-
-#include <asm/sbus.h>
-#include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/ptrace.h>
-#include <asm/pgtable.h>
-#include <asm/oplib.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#ifndef __sparc_v9__
-#include <asm/machines.h>
-#include <asm/idprom.h>
-#endif
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_eh.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_tcq.h>
-
-#define DRV_VERSION "1.101"
-
-#define DEBUG_ESP
-/* #define DEBUG_ESP_HME */
-/* #define DEBUG_ESP_DATA */
-/* #define DEBUG_ESP_QUEUE */
-/* #define DEBUG_ESP_DISCONNECT */
-/* #define DEBUG_ESP_STATUS */
-/* #define DEBUG_ESP_PHASES */
-/* #define DEBUG_ESP_WORKBUS */
-/* #define DEBUG_STATE_MACHINE */
-/* #define DEBUG_ESP_CMDS */
-/* #define DEBUG_ESP_IRQS */
-/* #define DEBUG_SDTR */
-/* #define DEBUG_ESP_SG */
-
-/* Use the following to sprinkle debugging messages in a way which
- * suits you if combinations of the above become too verbose when
- * trying to track down a specific problem.
- */
-/* #define DEBUG_ESP_MISC */
-
-#if defined(DEBUG_ESP)
-#define ESPLOG(foo) printk foo
-#else
-#define ESPLOG(foo)
-#endif /* (DEBUG_ESP) */
-
-#if defined(DEBUG_ESP_HME)
-#define ESPHME(foo) printk foo
-#else
-#define ESPHME(foo)
-#endif
-
-#if defined(DEBUG_ESP_DATA)
-#define ESPDATA(foo) printk foo
-#else
-#define ESPDATA(foo)
-#endif
-
-#if defined(DEBUG_ESP_QUEUE)
-#define ESPQUEUE(foo) printk foo
-#else
-#define ESPQUEUE(foo)
-#endif
-
-#if defined(DEBUG_ESP_DISCONNECT)
-#define ESPDISC(foo) printk foo
-#else
-#define ESPDISC(foo)
-#endif
-
-#if defined(DEBUG_ESP_STATUS)
-#define ESPSTAT(foo) printk foo
-#else
-#define ESPSTAT(foo)
-#endif
-
-#if defined(DEBUG_ESP_PHASES)
-#define ESPPHASE(foo) printk foo
-#else
-#define ESPPHASE(foo)
-#endif
-
-#if defined(DEBUG_ESP_WORKBUS)
-#define ESPBUS(foo) printk foo
-#else
-#define ESPBUS(foo)
-#endif
-
-#if defined(DEBUG_ESP_IRQS)
-#define ESPIRQ(foo) printk foo
-#else
-#define ESPIRQ(foo)
-#endif
-
-#if defined(DEBUG_SDTR)
-#define ESPSDTR(foo) printk foo
-#else
-#define ESPSDTR(foo)
-#endif
-
-#if defined(DEBUG_ESP_MISC)
-#define ESPMISC(foo) printk foo
-#else
-#define ESPMISC(foo)
-#endif
-
-/* Command phase enumeration. */
-enum {
- not_issued = 0x00, /* Still in the issue_SC queue. */
-
- /* Various forms of selecting a target. */
-#define in_slct_mask 0x10
- in_slct_norm = 0x10, /* ESP is arbitrating, normal selection */
- in_slct_stop = 0x11, /* ESP will select, then stop with IRQ */
- in_slct_msg = 0x12, /* select, then send a message */
- in_slct_tag = 0x13, /* select and send tagged queue msg */
- in_slct_sneg = 0x14, /* select and acquire sync capabilities */
-
- /* Any post selection activity. */
-#define in_phases_mask 0x20
- in_datain = 0x20, /* Data is transferring from the bus */
- in_dataout = 0x21, /* Data is transferring to the bus */
- in_data_done = 0x22, /* Last DMA data operation done (maybe) */
- in_msgin = 0x23, /* Eating message from target */
- in_msgincont = 0x24, /* Eating more msg bytes from target */
- in_msgindone = 0x25, /* Decide what to do with what we got */
- in_msgout = 0x26, /* Sending message to target */
- in_msgoutdone = 0x27, /* Done sending msg out */
- in_cmdbegin = 0x28, /* Sending cmd after abnormal selection */
- in_cmdend = 0x29, /* Done sending slow cmd */
- in_status = 0x2a, /* Was in status phase, finishing cmd */
- in_freeing = 0x2b, /* freeing the bus for cmd cmplt or disc */
- in_the_dark = 0x2c, /* Don't know what bus phase we are in */
-
- /* Special states, ie. not normal bus transitions... */
-#define in_spec_mask 0x80
- in_abortone = 0x80, /* Aborting one command currently */
- in_abortall = 0x81, /* Blowing away all commands we have */
- in_resetdev = 0x82, /* SCSI target reset in progress */
- in_resetbus = 0x83, /* SCSI bus reset in progress */
- in_tgterror = 0x84, /* Target did something stupid */
-};
-
-enum {
- /* Zero has special meaning, see skipahead[12]. */
-/*0*/ do_never,
-
-/*1*/ do_phase_determine,
-/*2*/ do_reset_bus,
-/*3*/ do_reset_complete,
-/*4*/ do_work_bus,
-/*5*/ do_intr_end
-};
-
-/* Forward declarations. */
-static irqreturn_t esp_intr(int irq, void *dev_id);
-
-/* Debugging routines */
-struct esp_cmdstrings {
- u8 cmdchar;
- char *text;
-} esp_cmd_strings[] = {
- /* Miscellaneous */
- { ESP_CMD_NULL, "ESP_NOP", },
- { ESP_CMD_FLUSH, "FIFO_FLUSH", },
- { ESP_CMD_RC, "RSTESP", },
- { ESP_CMD_RS, "RSTSCSI", },
- /* Disconnected State Group */
- { ESP_CMD_RSEL, "RESLCTSEQ", },
- { ESP_CMD_SEL, "SLCTNATN", },
- { ESP_CMD_SELA, "SLCTATN", },
- { ESP_CMD_SELAS, "SLCTATNSTOP", },
- { ESP_CMD_ESEL, "ENSLCTRESEL", },
- { ESP_CMD_DSEL, "DISSELRESEL", },
- { ESP_CMD_SA3, "SLCTATN3", },
- { ESP_CMD_RSEL3, "RESLCTSEQ", },
- /* Target State Group */
- { ESP_CMD_SMSG, "SNDMSG", },
- { ESP_CMD_SSTAT, "SNDSTATUS", },
- { ESP_CMD_SDATA, "SNDDATA", },
- { ESP_CMD_DSEQ, "DISCSEQ", },
- { ESP_CMD_TSEQ, "TERMSEQ", },
- { ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", },
- { ESP_CMD_DCNCT, "DISC", },
- { ESP_CMD_RMSG, "RCVMSG", },
- { ESP_CMD_RCMD, "RCVCMD", },
- { ESP_CMD_RDATA, "RCVDATA", },
- { ESP_CMD_RCSEQ, "RCVCMDSEQ", },
- /* Initiator State Group */
- { ESP_CMD_TI, "TRANSINFO", },
- { ESP_CMD_ICCSEQ, "INICMDSEQCOMP", },
- { ESP_CMD_MOK, "MSGACCEPTED", },
- { ESP_CMD_TPAD, "TPAD", },
- { ESP_CMD_SATN, "SATN", },
- { ESP_CMD_RATN, "RATN", },
-};
-#define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings)))
-
-/* Print textual representation of an ESP command */
-static inline void esp_print_cmd(u8 espcmd)
-{
- u8 dma_bit = espcmd & ESP_CMD_DMA;
- int i;
-
- espcmd &= ~dma_bit;
- for (i = 0; i < NUM_ESP_COMMANDS; i++)
- if (esp_cmd_strings[i].cmdchar == espcmd)
- break;
- if (i == NUM_ESP_COMMANDS)
- printk("ESP_Unknown");
- else
- printk("%s%s", esp_cmd_strings[i].text,
- ((dma_bit) ? "+DMA" : ""));
-}
-
-/* Print the status register's value */
-static inline void esp_print_statreg(u8 statreg)
-{
- u8 phase;
-
- printk("STATUS<");
- phase = statreg & ESP_STAT_PMASK;
- printk("%s,", (phase == ESP_DOP ? "DATA-OUT" :
- (phase == ESP_DIP ? "DATA-IN" :
- (phase == ESP_CMDP ? "COMMAND" :
- (phase == ESP_STATP ? "STATUS" :
- (phase == ESP_MOP ? "MSG-OUT" :
- (phase == ESP_MIP ? "MSG_IN" :
- "unknown")))))));
- if (statreg & ESP_STAT_TDONE)
- printk("TRANS_DONE,");
- if (statreg & ESP_STAT_TCNT)
- printk("TCOUNT_ZERO,");
- if (statreg & ESP_STAT_PERR)
- printk("P_ERROR,");
- if (statreg & ESP_STAT_SPAM)
- printk("SPAM,");
- if (statreg & ESP_STAT_INTR)
- printk("IRQ,");
- printk(">");
-}
-
-/* Print the interrupt register's value */
-static inline void esp_print_ireg(u8 intreg)
-{
- printk("INTREG< ");
- if (intreg & ESP_INTR_S)
- printk("SLCT_NATN ");
- if (intreg & ESP_INTR_SATN)
- printk("SLCT_ATN ");
- if (intreg & ESP_INTR_RSEL)
- printk("RSLCT ");
- if (intreg & ESP_INTR_FDONE)
- printk("FDONE ");
- if (intreg & ESP_INTR_BSERV)
- printk("BSERV ");
- if (intreg & ESP_INTR_DC)
- printk("DISCNCT ");
- if (intreg & ESP_INTR_IC)
- printk("ILL_CMD ");
- if (intreg & ESP_INTR_SR)
- printk("SCSI_BUS_RESET ");
- printk(">");
-}
-
-/* Print the sequence step registers contents */
-static inline void esp_print_seqreg(u8 stepreg)
-{
- stepreg &= ESP_STEP_VBITS;
- printk("STEP<%s>",
- (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" :
- (stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" :
- (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" :
- (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" :
- (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" :
- "UNKNOWN"))))));
-}
-
-static char *phase_string(int phase)
-{
- switch (phase) {
- case not_issued:
- return "UNISSUED";
- case in_slct_norm:
- return "SLCTNORM";
- case in_slct_stop:
- return "SLCTSTOP";
- case in_slct_msg:
- return "SLCTMSG";
- case in_slct_tag:
- return "SLCTTAG";
- case in_slct_sneg:
- return "SLCTSNEG";
- case in_datain:
- return "DATAIN";
- case in_dataout:
- return "DATAOUT";
- case in_data_done:
- return "DATADONE";
- case in_msgin:
- return "MSGIN";
- case in_msgincont:
- return "MSGINCONT";
- case in_msgindone:
- return "MSGINDONE";
- case in_msgout:
- return "MSGOUT";
- case in_msgoutdone:
- return "MSGOUTDONE";
- case in_cmdbegin:
- return "CMDBEGIN";
- case in_cmdend:
- return "CMDEND";
- case in_status:
- return "STATUS";
- case in_freeing:
- return "FREEING";
- case in_the_dark:
- return "CLUELESS";
- case in_abortone:
- return "ABORTONE";
- case in_abortall:
- return "ABORTALL";
- case in_resetdev:
- return "RESETDEV";
- case in_resetbus:
- return "RESETBUS";
- case in_tgterror:
- return "TGTERROR";
- default:
- return "UNKNOWN";
- };
-}
-
-#ifdef DEBUG_STATE_MACHINE
-static inline void esp_advance_phase(struct scsi_cmnd *s, int newphase)
-{
- ESPLOG(("<%s>", phase_string(newphase)));
- s->SCp.sent_command = s->SCp.phase;
- s->SCp.phase = newphase;
-}
-#else
-#define esp_advance_phase(__s, __newphase) \
- (__s)->SCp.sent_command = (__s)->SCp.phase; \
- (__s)->SCp.phase = (__newphase);
-#endif
-
-#ifdef DEBUG_ESP_CMDS
-static inline void esp_cmd(struct esp *esp, u8 cmd)
-{
- esp->espcmdlog[esp->espcmdent] = cmd;
- esp->espcmdent = (esp->espcmdent + 1) & 31;
- sbus_writeb(cmd, esp->eregs + ESP_CMD);
-}
-#else
-#define esp_cmd(__esp, __cmd) \
- sbus_writeb((__cmd), ((__esp)->eregs) + ESP_CMD)
-#endif
-
-#define ESP_INTSOFF(__dregs) \
- sbus_writel(sbus_readl((__dregs)+DMA_CSR)&~(DMA_INT_ENAB), (__dregs)+DMA_CSR)
-#define ESP_INTSON(__dregs) \
- sbus_writel(sbus_readl((__dregs)+DMA_CSR)|DMA_INT_ENAB, (__dregs)+DMA_CSR)
-#define ESP_IRQ_P(__dregs) \
- (sbus_readl((__dregs)+DMA_CSR) & (DMA_HNDL_INTR|DMA_HNDL_ERROR))
-
-/* How we use the various Linux SCSI data structures for operation.
- *
- * struct scsi_cmnd:
- *
- * We keep track of the synchronous capabilities of a target
- * in the device member, using sync_min_period and
- * sync_max_offset. These are the values we directly write
- * into the ESP registers while running a command. If offset
- * is zero the ESP will use asynchronous transfers.
- * If the borken flag is set we assume we shouldn't even bother
- * trying to negotiate for synchronous transfer as this target
- * is really stupid. If we notice the target is dropping the
- * bus, and we have been allowing it to disconnect, we clear
- * the disconnect flag.
- */
-
-
-/* Manipulation of the ESP command queues. Thanks to the aha152x driver
- * and its author, Juergen E. Fischer, for the methods used here.
- * Note that these are per-ESP queues, not global queues like
- * the aha152x driver uses.
- */
-static inline void append_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
-{
- struct scsi_cmnd *end;
-
- new_SC->host_scribble = (unsigned char *) NULL;
- if (!*SC)
- *SC = new_SC;
- else {
- for (end=*SC;end->host_scribble;end=(struct scsi_cmnd *)end->host_scribble)
- ;
- end->host_scribble = (unsigned char *) new_SC;
- }
-}
-
-static inline void prepend_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
-{
- new_SC->host_scribble = (unsigned char *) *SC;
- *SC = new_SC;
-}
-
-static inline struct scsi_cmnd *remove_first_SC(struct scsi_cmnd **SC)
-{
- struct scsi_cmnd *ptr;
- ptr = *SC;
- if (ptr)
- *SC = (struct scsi_cmnd *) (*SC)->host_scribble;
- return ptr;
-}
-
-static inline struct scsi_cmnd *remove_SC(struct scsi_cmnd **SC, int target, int lun)
-{
- struct scsi_cmnd *ptr, *prev;
-
- for (ptr = *SC, prev = NULL;
- ptr && ((ptr->device->id != target) || (ptr->device->lun != lun));
- prev = ptr, ptr = (struct scsi_cmnd *) ptr->host_scribble)
- ;
- if (ptr) {
- if (prev)
- prev->host_scribble=ptr->host_scribble;
- else
- *SC=(struct scsi_cmnd *)ptr->host_scribble;
- }
- return ptr;
-}
-
-/* Resetting various pieces of the ESP scsi driver chipset/buses. */
-static void esp_reset_dma(struct esp *esp)
-{
- int can_do_burst16, can_do_burst32, can_do_burst64;
- int can_do_sbus64;
- u32 tmp;
-
- can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
- can_do_burst32 = (esp->bursts & DMA_BURST32) != 0;
- can_do_burst64 = 0;
- can_do_sbus64 = 0;
- if (sbus_can_dma_64bit(esp->sdev))
- can_do_sbus64 = 1;
- if (sbus_can_burst64(esp->sdev))
- can_do_burst64 = (esp->bursts & DMA_BURST64) != 0;
-
- /* Punt the DVMA into a known state. */
- if (esp->dma->revision != dvmahme) {
- tmp = sbus_readl(esp->dregs + DMA_CSR);
- sbus_writel(tmp | DMA_RST_SCSI, esp->dregs + DMA_CSR);
- sbus_writel(tmp & ~DMA_RST_SCSI, esp->dregs + DMA_CSR);
- }
- switch (esp->dma->revision) {
- case dvmahme:
- /* This is the HME DVMA gate array. */
-
- sbus_writel(DMA_RESET_FAS366, esp->dregs + DMA_CSR);
- sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR);
-
- esp->prev_hme_dmacsr = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB);
- esp->prev_hme_dmacsr &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ);
-
- if (can_do_burst64)
- esp->prev_hme_dmacsr |= DMA_BRST64;
- else if (can_do_burst32)
- esp->prev_hme_dmacsr |= DMA_BRST32;
-
- if (can_do_sbus64) {
- esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64;
- sbus_set_sbus64(esp->sdev, esp->bursts);
- }
-
- /* This chip is horrible. */
- while (sbus_readl(esp->dregs + DMA_CSR) & DMA_PEND_READ)
- udelay(1);
-
- sbus_writel(0, esp->dregs + DMA_CSR);
- sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
-
- /* This is necessary to avoid having the SCSI channel
- * engine lock up on us.
- */
- sbus_writel(0, esp->dregs + DMA_ADDR);
-
- break;
- case dvmarev2:
- /* This is the gate array found in the sun4m
- * NCR SBUS I/O subsystem.
- */
- if (esp->erev != esp100) {
- tmp = sbus_readl(esp->dregs + DMA_CSR);
- sbus_writel(tmp | DMA_3CLKS, esp->dregs + DMA_CSR);
- }
- break;
- case dvmarev3:
- tmp = sbus_readl(esp->dregs + DMA_CSR);
- tmp &= ~DMA_3CLKS;
- tmp |= DMA_2CLKS;
- if (can_do_burst32) {
- tmp &= ~DMA_BRST_SZ;
- tmp |= DMA_BRST32;
- }
- sbus_writel(tmp, esp->dregs + DMA_CSR);
- break;
- case dvmaesc1:
- /* This is the DMA unit found on SCSI/Ether cards. */
- tmp = sbus_readl(esp->dregs + DMA_CSR);
- tmp |= DMA_ADD_ENABLE;
- tmp &= ~DMA_BCNT_ENAB;
- if (!can_do_burst32 && can_do_burst16) {
- tmp |= DMA_ESC_BURST;
- } else {
- tmp &= ~(DMA_ESC_BURST);
- }
- sbus_writel(tmp, esp->dregs + DMA_CSR);
- break;
- default:
- break;
- };
- ESP_INTSON(esp->dregs);
-}
-
-/* Reset the ESP chip, _not_ the SCSI bus. */
-static void __init esp_reset_esp(struct esp *esp)
-{
- u8 family_code, version;
- int i;
-
- /* Now reset the ESP chip */
- esp_cmd(esp, ESP_CMD_RC);
- esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
- esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
-
- /* Reload the configuration registers */
- sbus_writeb(esp->cfact, esp->eregs + ESP_CFACT);
- esp->prev_stp = 0;
- sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
- esp->prev_soff = 0;
- sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
- sbus_writeb(esp->neg_defp, esp->eregs + ESP_TIMEO);
-
- /* This is the only point at which it is reliable to read
- * the ID-code for a fast ESP chip variants.
- */
- esp->max_period = ((35 * esp->ccycle) / 1000);
- if (esp->erev == fast) {
- version = sbus_readb(esp->eregs + ESP_UID);
- family_code = (version & 0xf8) >> 3;
- if (family_code == 0x02)
- esp->erev = fas236;
- else if (family_code == 0x0a)
- esp->erev = fashme; /* Version is usually '5'. */
- else
- esp->erev = fas100a;
- ESPMISC(("esp%d: FAST chip is %s (family=%d, version=%d)\n",
- esp->esp_id,
- (esp->erev == fas236) ? "fas236" :
- ((esp->erev == fas100a) ? "fas100a" :
- "fasHME"), family_code, (version & 7)));
-
- esp->min_period = ((4 * esp->ccycle) / 1000);
- } else {
- esp->min_period = ((5 * esp->ccycle) / 1000);
- }
- esp->max_period = (esp->max_period + 3)>>2;
- esp->min_period = (esp->min_period + 3)>>2;
-
- sbus_writeb(esp->config1, esp->eregs + ESP_CFG1);
- switch (esp->erev) {
- case esp100:
- /* nothing to do */
- break;
- case esp100a:
- sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
- break;
- case esp236:
- /* Slow 236 */
- sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
- esp->prev_cfg3 = esp->config3[0];
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
- break;
- case fashme:
- esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
- /* fallthrough... */
- case fas236:
- /* Fast 236 or HME */
- sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
- for (i = 0; i < 16; i++) {
- if (esp->erev == fashme) {
- u8 cfg3;
-
- cfg3 = ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH;
- if (esp->scsi_id >= 8)
- cfg3 |= ESP_CONFIG3_IDBIT3;
- esp->config3[i] |= cfg3;
- } else {
- esp->config3[i] |= ESP_CONFIG3_FCLK;
- }
- }
- esp->prev_cfg3 = esp->config3[0];
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
- if (esp->erev == fashme) {
- esp->radelay = 80;
- } else {
- if (esp->diff)
- esp->radelay = 0;
- else
- esp->radelay = 96;
- }
- break;
- case fas100a:
- /* Fast 100a */
- sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
- for (i = 0; i < 16; i++)
- esp->config3[i] |= ESP_CONFIG3_FCLOCK;
- esp->prev_cfg3 = esp->config3[0];
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
- esp->radelay = 32;
- break;
- default:
- panic("esp: what could it be... I wonder...");
- break;
- };
-
- /* Eat any bitrot in the chip */
- sbus_readb(esp->eregs + ESP_INTRPT);
- udelay(100);
-}
-
-/* This places the ESP into a known state at boot time. */
-static void __init esp_bootup_reset(struct esp *esp)
-{
- u8 tmp;
-
- /* Reset the DMA */
- esp_reset_dma(esp);
-
- /* Reset the ESP */
- esp_reset_esp(esp);
-
- /* Reset the SCSI bus, but tell ESP not to generate an irq */
- tmp = sbus_readb(esp->eregs + ESP_CFG1);
- tmp |= ESP_CONFIG1_SRRDISAB;
- sbus_writeb(tmp, esp->eregs + ESP_CFG1);
-
- esp_cmd(esp, ESP_CMD_RS);
- udelay(400);
-
- sbus_writeb(esp->config1, esp->eregs + ESP_CFG1);
-
- /* Eat any bitrot in the chip and we are done... */
- sbus_readb(esp->eregs + ESP_INTRPT);
-}
-
-static int __init esp_find_dvma(struct esp *esp, struct sbus_dev *dma_sdev)
-{
- struct sbus_dev *sdev = esp->sdev;
- struct sbus_dma *dma;
-
- if (dma_sdev != NULL) {
- for_each_dvma(dma) {
- if (dma->sdev == dma_sdev)
- break;
- }
- } else {
- for_each_dvma(dma) {
- /* If allocated already, can't use it. */
- if (dma->allocated)
- continue;
-
- if (dma->sdev == NULL)
- break;
-
- /* If bus + slot are the same and it has the
- * correct OBP name, it's ours.
- */
- if (sdev->bus == dma->sdev->bus &&
- sdev->slot == dma->sdev->slot &&
- (!strcmp(dma->sdev->prom_name, "dma") ||
- !strcmp(dma->sdev->prom_name, "espdma")))
- break;
- }
- }
-
- /* If we don't know how to handle the dvma,
- * do not use this device.
- */
- if (dma == NULL) {
- printk("Cannot find dvma for ESP%d's SCSI\n", esp->esp_id);
- return -1;
- }
- if (dma->allocated) {
- printk("esp%d: can't use my espdma\n", esp->esp_id);
- return -1;
- }
- dma->allocated = 1;
- esp->dma = dma;
- esp->dregs = dma->regs;
-
- return 0;
-}
-
-static int __init esp_map_regs(struct esp *esp, int hme)
-{
- struct sbus_dev *sdev = esp->sdev;
- struct resource *res;
-
- /* On HME, two reg sets exist, first is DVMA,
- * second is ESP registers.
- */
- if (hme)
- res = &sdev->resource[1];
- else
- res = &sdev->resource[0];
-
- esp->eregs = sbus_ioremap(res, 0, ESP_REG_SIZE, "ESP Registers");
-
- if (esp->eregs == 0)
- return -1;
- return 0;
-}
-
-static int __init esp_map_cmdarea(struct esp *esp)
-{
- struct sbus_dev *sdev = esp->sdev;
-
- esp->esp_command = sbus_alloc_consistent(sdev, 16,
- &esp->esp_command_dvma);
- if (esp->esp_command == NULL ||
- esp->esp_command_dvma == 0)
- return -1;
- return 0;
-}
-
-static int __init esp_register_irq(struct esp *esp)
-{
- esp->ehost->irq = esp->irq = esp->sdev->irqs[0];
-
- /* We used to try various overly-clever things to
- * reduce the interrupt processing overhead on
- * sun4c/sun4m when multiple ESP's shared the
- * same IRQ. It was too complex and messy to
- * sanely maintain.
- */
- if (request_irq(esp->ehost->irq, esp_intr,
- IRQF_SHARED, "ESP SCSI", esp)) {
- printk("esp%d: Cannot acquire irq line\n",
- esp->esp_id);
- return -1;
- }
-
- printk("esp%d: IRQ %d ", esp->esp_id,
- esp->ehost->irq);
-
- return 0;
-}
-
-static void __init esp_get_scsi_id(struct esp *esp)
-{
- struct sbus_dev *sdev = esp->sdev;
- struct device_node *dp = sdev->ofdev.node;
-
- esp->scsi_id = of_getintprop_default(dp,
- "initiator-id",
- -1);
- if (esp->scsi_id == -1)
- esp->scsi_id = of_getintprop_default(dp,
- "scsi-initiator-id",
- -1);
- if (esp->scsi_id == -1)
- esp->scsi_id = (sdev->bus == NULL) ? 7 :
- of_getintprop_default(sdev->bus->ofdev.node,
- "scsi-initiator-id",
- 7);
- esp->ehost->this_id = esp->scsi_id;
- esp->scsi_id_mask = (1 << esp->scsi_id);
-
-}
-
-static void __init esp_get_clock_params(struct esp *esp)
-{
- struct sbus_dev *sdev = esp->sdev;
- int prom_node = esp->prom_node;
- int sbus_prom_node;
- unsigned int fmhz;
- u8 ccf;
-
- if (sdev != NULL && sdev->bus != NULL)
- sbus_prom_node = sdev->bus->prom_node;
- else
- sbus_prom_node = 0;
-
- /* This is getting messy but it has to be done
- * correctly or else you get weird behavior all
- * over the place. We are trying to basically
- * figure out three pieces of information.
- *
- * a) Clock Conversion Factor
- *
- * This is a representation of the input
- * crystal clock frequency going into the
- * ESP on this machine. Any operation whose
- * timing is longer than 400ns depends on this
- * value being correct. For example, you'll
- * get blips for arbitration/selection during
- * high load or with multiple targets if this
- * is not set correctly.
- *
- * b) Selection Time-Out
- *
- * The ESP isn't very bright and will arbitrate
- * for the bus and try to select a target
- * forever if you let it. This value tells
- * the ESP when it has taken too long to
- * negotiate and that it should interrupt
- * the CPU so we can see what happened.
- * The value is computed as follows (from
- * NCR/Symbios chip docs).
- *
- * (Time Out Period) * (Input Clock)
- * STO = ----------------------------------
- * (8192) * (Clock Conversion Factor)
- *
- * You usually want the time out period to be
- * around 250ms, I think we'll set it a little
- * bit higher to account for fully loaded SCSI
- * bus's and slow devices that don't respond so
- * quickly to selection attempts. (yeah, I know
- * this is out of spec. but there is a lot of
- * buggy pieces of firmware out there so bite me)
- *
- * c) Imperical constants for synchronous offset
- * and transfer period register values
- *
- * This entails the smallest and largest sync
- * period we could ever handle on this ESP.
- */
-
- fmhz = prom_getintdefault(prom_node, "clock-frequency", -1);
- if (fmhz == -1)
- fmhz = (!sbus_prom_node) ? 0 :
- prom_getintdefault(sbus_prom_node, "clock-frequency", -1);
-
- if (fmhz <= (5000000))
- ccf = 0;
- else
- ccf = (((5000000 - 1) + (fmhz))/(5000000));
-
- if (!ccf || ccf > 8) {
- /* If we can't find anything reasonable,
- * just assume 20MHZ. This is the clock
- * frequency of the older sun4c's where I've
- * been unable to find the clock-frequency
- * PROM property. All other machines provide
- * useful values it seems.
- */
- ccf = ESP_CCF_F4;
- fmhz = (20000000);
- }
-
- if (ccf == (ESP_CCF_F7 + 1))
- esp->cfact = ESP_CCF_F0;
- else if (ccf == ESP_CCF_NEVER)
- esp->cfact = ESP_CCF_F2;
- else
- esp->cfact = ccf;
- esp->raw_cfact = ccf;
-
- esp->cfreq = fmhz;
- esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
- esp->ctick = ESP_TICK(ccf, esp->ccycle);
- esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
- esp->sync_defp = SYNC_DEFP_SLOW;
-
- printk("SCSI ID %d Clk %dMHz CCYC=%d CCF=%d TOut %d ",
- esp->scsi_id, (fmhz / 1000000),
- (int)esp->ccycle, (int)ccf, (int) esp->neg_defp);
-}
-
-static void __init esp_get_bursts(struct esp *esp, struct sbus_dev *dma)
-{
- struct sbus_dev *sdev = esp->sdev;
- u8 bursts;
-
- bursts = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff);
-
- if (dma) {
- u8 tmp = prom_getintdefault(dma->prom_node,
- "burst-sizes", 0xff);
- if (tmp != 0xff)
- bursts &= tmp;
- }
-
- if (sdev->bus) {
- u8 tmp = prom_getintdefault(sdev->bus->prom_node,
- "burst-sizes", 0xff);
- if (tmp != 0xff)
- bursts &= tmp;
- }
-
- if (bursts == 0xff ||
- (bursts & DMA_BURST16) == 0 ||
- (bursts & DMA_BURST32) == 0)
- bursts = (DMA_BURST32 - 1);
-
- esp->bursts = bursts;
-}
-
-static void __init esp_get_revision(struct esp *esp)
-{
- u8 tmp;
-
- esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
- esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
- sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
-
- tmp = sbus_readb(esp->eregs + ESP_CFG2);
- tmp &= ~ESP_CONFIG2_MAGIC;
- if (tmp != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
- /* If what we write to cfg2 does not come back, cfg2
- * is not implemented, therefore this must be a plain
- * esp100.
- */
- esp->erev = esp100;
- printk("NCR53C90(esp100)\n");
- } else {
- esp->config2 = 0;
- esp->prev_cfg3 = esp->config3[0] = 5;
- sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
- sbus_writeb(0, esp->eregs + ESP_CFG3);
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-
- tmp = sbus_readb(esp->eregs + ESP_CFG3);
- if (tmp != 5) {
- /* The cfg2 register is implemented, however
- * cfg3 is not, must be esp100a.
- */
- esp->erev = esp100a;
- printk("NCR53C90A(esp100a)\n");
- } else {
- int target;
-
- for (target = 0; target < 16; target++)
- esp->config3[target] = 0;
- esp->prev_cfg3 = 0;
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-
- /* All of cfg{1,2,3} implemented, must be one of
- * the fas variants, figure out which one.
- */
- if (esp->raw_cfact > ESP_CCF_F5) {
- esp->erev = fast;
- esp->sync_defp = SYNC_DEFP_FAST;
- printk("NCR53C9XF(espfast)\n");
- } else {
- esp->erev = esp236;
- printk("NCR53C9x(esp236)\n");
- }
- esp->config2 = 0;
- sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
- }
- }
-}
-
-static void __init esp_init_swstate(struct esp *esp)
-{
- int i;
-
- /* Command queues... */
- esp->current_SC = NULL;
- esp->disconnected_SC = NULL;
- esp->issue_SC = NULL;
-
- /* Target and current command state... */
- esp->targets_present = 0;
- esp->resetting_bus = 0;
- esp->snip = 0;
-
- init_waitqueue_head(&esp->reset_queue);
-
- /* Debugging... */
- for(i = 0; i < 32; i++)
- esp->espcmdlog[i] = 0;
- esp->espcmdent = 0;
-
- /* MSG phase state... */
- for(i = 0; i < 16; i++) {
- esp->cur_msgout[i] = 0;
- esp->cur_msgin[i] = 0;
- }
- esp->prevmsgout = esp->prevmsgin = 0;
- esp->msgout_len = esp->msgin_len = 0;
-
- /* Clear the one behind caches to hold unmatchable values. */
- esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff;
- esp->prev_hme_dmacsr = 0xffffffff;
-}
-
-static int __init detect_one_esp(struct scsi_host_template *tpnt,
- struct device *dev,
- struct sbus_dev *esp_dev,
- struct sbus_dev *espdma,
- struct sbus_bus *sbus,
- int hme)
-{
- static int instance;
- struct Scsi_Host *esp_host = scsi_host_alloc(tpnt, sizeof(struct esp));
- struct esp *esp;
-
- if (!esp_host)
- return -ENOMEM;
-
- if (hme)
- esp_host->max_id = 16;
- esp = (struct esp *) esp_host->hostdata;
- esp->ehost = esp_host;
- esp->sdev = esp_dev;
- esp->esp_id = instance;
- esp->prom_node = esp_dev->prom_node;
- prom_getstring(esp->prom_node, "name", esp->prom_name,
- sizeof(esp->prom_name));
-
- if (esp_find_dvma(esp, espdma) < 0)
- goto fail_unlink;
- if (esp_map_regs(esp, hme) < 0) {
- printk("ESP registers unmappable");
- goto fail_dvma_release;
- }
- if (esp_map_cmdarea(esp) < 0) {
- printk("ESP DVMA transport area unmappable");
- goto fail_unmap_regs;
- }
- if (esp_register_irq(esp) < 0)
- goto fail_unmap_cmdarea;
-
- esp_get_scsi_id(esp);
-
- esp->diff = prom_getbool(esp->prom_node, "differential");
- if (esp->diff)
- printk("Differential ");
-
- esp_get_clock_params(esp);
- esp_get_bursts(esp, espdma);
- esp_get_revision(esp);
- esp_init_swstate(esp);
-
- esp_bootup_reset(esp);
-
- if (scsi_add_host(esp_host, dev))
- goto fail_free_irq;
-
- dev_set_drvdata(&esp_dev->ofdev.dev, esp);
-
- scsi_scan_host(esp_host);
- instance++;
-
- return 0;
-
-fail_free_irq:
- free_irq(esp->ehost->irq, esp);
-
-fail_unmap_cmdarea:
- sbus_free_consistent(esp->sdev, 16,
- (void *) esp->esp_command,
- esp->esp_command_dvma);
-
-fail_unmap_regs:
- sbus_iounmap(esp->eregs, ESP_REG_SIZE);
-
-fail_dvma_release:
- esp->dma->allocated = 0;
-
-fail_unlink:
- scsi_host_put(esp_host);
- return -1;
-}
-
-/* Detecting ESP chips on the machine. This is the simple and easy
- * version.
- */
-static int __devexit esp_remove_common(struct esp *esp)
-{
- unsigned int irq = esp->ehost->irq;
-
- scsi_remove_host(esp->ehost);
-
- ESP_INTSOFF(esp->dregs);
-#if 0
- esp_reset_dma(esp);
- esp_reset_esp(esp);
-#endif
-
- free_irq(irq, esp);
- sbus_free_consistent(esp->sdev, 16,
- (void *) esp->esp_command, esp->esp_command_dvma);
- sbus_iounmap(esp->eregs, ESP_REG_SIZE);
- esp->dma->allocated = 0;
-
- scsi_host_put(esp->ehost);
-
- return 0;
-}
-
-
-#ifdef CONFIG_SUN4
-
-#include <asm/sun4paddr.h>
-
-static struct sbus_dev sun4_esp_dev;
-
-static int __init esp_sun4_probe(struct scsi_host_template *tpnt)
-{
- if (sun4_esp_physaddr) {
- memset(&sun4_esp_dev, 0, sizeof(sun4_esp_dev));
- sun4_esp_dev.reg_addrs[0].phys_addr = sun4_esp_physaddr;
- sun4_esp_dev.irqs[0] = 4;
- sun4_esp_dev.resource[0].start = sun4_esp_physaddr;
- sun4_esp_dev.resource[0].end =
- sun4_esp_physaddr + ESP_REG_SIZE - 1;
- sun4_esp_dev.resource[0].flags = IORESOURCE_IO;
-
- return detect_one_esp(tpnt, NULL,
- &sun4_esp_dev, NULL, NULL, 0);
- }
- return 0;
-}
-
-static int __devexit esp_sun4_remove(void)
-{
- struct of_device *dev = &sun4_esp_dev.ofdev;
- struct esp *esp = dev_get_drvdata(&dev->dev);
-
- return esp_remove_common(esp);
-}
-
-#else /* !CONFIG_SUN4 */
-
-static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match)
-{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
- struct device_node *dp = dev->node;
- struct sbus_dev *dma_sdev = NULL;
- int hme = 0;
-
- if (dp->parent &&
- (!strcmp(dp->parent->name, "espdma") ||
- !strcmp(dp->parent->name, "dma")))
- dma_sdev = sdev->parent;
- else if (!strcmp(dp->name, "SUNW,fas")) {
- dma_sdev = sdev;
- hme = 1;
- }
-
- return detect_one_esp(match->data, &dev->dev,
- sdev, dma_sdev, sdev->bus, hme);
-}
-
-static int __devexit esp_sbus_remove(struct of_device *dev)
-{
- struct esp *esp = dev_get_drvdata(&dev->dev);
-
- return esp_remove_common(esp);
-}
-
-#endif /* !CONFIG_SUN4 */
-
-/* The info function will return whatever useful
- * information the developer sees fit. If not provided, then
- * the name field will be used instead.
- */
-static const char *esp_info(struct Scsi_Host *host)
-{
- struct esp *esp;
-
- esp = (struct esp *) host->hostdata;
- switch (esp->erev) {
- case esp100:
- return "Sparc ESP100 (NCR53C90)";
- case esp100a:
- return "Sparc ESP100A (NCR53C90A)";
- case esp236:
- return "Sparc ESP236";
- case fas236:
- return "Sparc ESP236-FAST";
- case fashme:
- return "Sparc ESP366-HME";
- case fas100a:
- return "Sparc ESP100A-FAST";
- default:
- return "Bogon ESP revision";
- };
-}
-
-/* From Wolfgang Stanglmeier's NCR scsi driver. */
-struct info_str
-{
- char *buffer;
- int length;
- int offset;
- int pos;
-};
-
-static void copy_mem_info(struct info_str *info, char *data, int len)
-{
- if (info->pos + len > info->length)
- len = info->length - info->pos;
-
- if (info->pos + len < info->offset) {
- info->pos += len;
- return;
- }
- if (info->pos < info->offset) {
- data += (info->offset - info->pos);
- len -= (info->offset - info->pos);
- }
-
- if (len > 0) {
- memcpy(info->buffer + info->pos, data, len);
- info->pos += len;
- }
-}
-
-static int copy_info(struct info_str *info, char *fmt, ...)
-{
- va_list args;
- char buf[81];
- int len;
-
- va_start(args, fmt);
- len = vsprintf(buf, fmt, args);
- va_end(args);
-
- copy_mem_info(info, buf, len);
- return len;
-}
-
-static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len)
-{
- struct scsi_device *sdev;
- struct info_str info;
- int i;
-
- info.buffer = ptr;
- info.length = len;
- info.offset = offset;
- info.pos = 0;
-
- copy_info(&info, "Sparc ESP Host Adapter:\n");
- copy_info(&info, "\tPROM node\t\t%08x\n", (unsigned int) esp->prom_node);
- copy_info(&info, "\tPROM name\t\t%s\n", esp->prom_name);
- copy_info(&info, "\tESP Model\t\t");
- switch (esp->erev) {
- case esp100:
- copy_info(&info, "ESP100\n");
- break;
- case esp100a:
- copy_info(&info, "ESP100A\n");
- break;
- case esp236:
- copy_info(&info, "ESP236\n");
- break;
- case fas236:
- copy_info(&info, "FAS236\n");
- break;
- case fas100a:
- copy_info(&info, "FAS100A\n");
- break;
- case fast:
- copy_info(&info, "FAST\n");
- break;
- case fashme:
- copy_info(&info, "Happy Meal FAS\n");
- break;
- case espunknown:
- default:
- copy_info(&info, "Unknown!\n");
- break;
- };
- copy_info(&info, "\tDMA Revision\t\t");
- switch (esp->dma->revision) {
- case dvmarev0:
- copy_info(&info, "Rev 0\n");
- break;
- case dvmaesc1:
- copy_info(&info, "ESC Rev 1\n");
- break;
- case dvmarev1:
- copy_info(&info, "Rev 1\n");
- break;
- case dvmarev2:
- copy_info(&info, "Rev 2\n");
- break;
- case dvmarev3:
- copy_info(&info, "Rev 3\n");
- break;
- case dvmarevplus:
- copy_info(&info, "Rev 1+\n");
- break;
- case dvmahme:
- copy_info(&info, "Rev HME/FAS\n");
- break;
- default:
- copy_info(&info, "Unknown!\n");
- break;
- };
- copy_info(&info, "\tLive Targets\t\t[ ");
- for (i = 0; i < 15; i++) {
- if (esp->targets_present & (1 << i))
- copy_info(&info, "%d ", i);
- }
- copy_info(&info, "]\n\n");
-
- /* Now describe the state of each existing target. */
- copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n");
-
- shost_for_each_device(sdev, esp->ehost) {
- struct esp_device *esp_dev = sdev->hostdata;
- uint id = sdev->id;
-
- if (!(esp->targets_present & (1 << id)))
- continue;
-
- copy_info(&info, "%d\t\t", id);
- copy_info(&info, "%08lx\t", esp->config3[id]);
- copy_info(&info, "[%02lx,%02lx]\t\t\t",
- esp_dev->sync_max_offset,
- esp_dev->sync_min_period);
- copy_info(&info, "%s\t\t",
- esp_dev->disconnect ? "yes" : "no");
- copy_info(&info, "%s\n",
- (esp->config3[id] & ESP_CONFIG3_EWIDE) ? "yes" : "no");
- }
- return info.pos > info.offset? info.pos - info.offset : 0;
-}
-
-/* ESP proc filesystem code. */
-static int esp_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
- int length, int inout)
-{
- struct esp *esp = (struct esp *) host->hostdata;
-
- if (inout)
- return -EINVAL; /* not yet */
-
- if (start)
- *start = buffer;
-
- return esp_host_info(esp, buffer, offset, length);
-}
-
-static void esp_get_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
-{
- if (sp->use_sg == 0) {
- sp->SCp.this_residual = sp->request_bufflen;
- sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
- sp->SCp.buffers_residual = 0;
- if (sp->request_bufflen) {
- sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer,
- sp->SCp.this_residual,
- sp->sc_data_direction);
- sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in);
- } else {
- sp->SCp.ptr = NULL;
- }
- } else {
- sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
- sp->SCp.buffers_residual = sbus_map_sg(esp->sdev,
- sp->SCp.buffer,
- sp->use_sg,
- sp->sc_data_direction);
- sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer);
- sp->SCp.ptr = (char *) ((unsigned long)sg_dma_address(sp->SCp.buffer));
- }
-}
-
-static void esp_release_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
-{
- if (sp->use_sg) {
- sbus_unmap_sg(esp->sdev, sp->request_buffer, sp->use_sg,
- sp->sc_data_direction);
- } else if (sp->request_bufflen) {
- sbus_unmap_single(esp->sdev,
- sp->SCp.have_data_in,
- sp->request_bufflen,
- sp->sc_data_direction);
- }
-}
-
-static void esp_restore_pointers(struct esp *esp, struct scsi_cmnd *sp)
-{
- struct esp_pointers *ep = &esp->data_pointers[sp->device->id];
-
- sp->SCp.ptr = ep->saved_ptr;
- sp->SCp.buffer = ep->saved_buffer;
- sp->SCp.this_residual = ep->saved_this_residual;
- sp->SCp.buffers_residual = ep->saved_buffers_residual;
-}
-
-static void esp_save_pointers(struct esp *esp, struct scsi_cmnd *sp)
-{
- struct esp_pointers *ep = &esp->data_pointers[sp->device->id];
-
- ep->saved_ptr = sp->SCp.ptr;
- ep->saved_buffer = sp->SCp.buffer;
- ep->saved_this_residual = sp->SCp.this_residual;
- ep->saved_buffers_residual = sp->SCp.buffers_residual;
-}
-
-/* Some rules:
- *
- * 1) Never ever panic while something is live on the bus.
- * If there is to be any chance of syncing the disks this
- * rule is to be obeyed.
- *
- * 2) Any target that causes a foul condition will no longer
- * have synchronous transfers done to it, no questions
- * asked.
- *
- * 3) Keep register accesses to a minimum. Think about some
- * day when we have Xbus machines this is running on and
- * the ESP chip is on the other end of the machine on a
- * different board from the cpu where this is running.
- */
-
-/* Fire off a command. We assume the bus is free and that the only
- * case where we could see an interrupt is where we have disconnected
- * commands active and they are trying to reselect us.
- */
-static inline void esp_check_cmd(struct esp *esp, struct scsi_cmnd *sp)
-{
- switch (sp->cmd_len) {
- case 6:
- case 10:
- case 12:
- esp->esp_slowcmd = 0;
- break;
-
- default:
- esp->esp_slowcmd = 1;
- esp->esp_scmdleft = sp->cmd_len;
- esp->esp_scmdp = &sp->cmnd[0];
- break;
- };
-}
-
-static inline void build_sync_nego_msg(struct esp *esp, int period, int offset)
-{
- esp->cur_msgout[0] = EXTENDED_MESSAGE;
- esp->cur_msgout[1] = 3;
- esp->cur_msgout[2] = EXTENDED_SDTR;
- esp->cur_msgout[3] = period;
- esp->cur_msgout[4] = offset;
- esp->msgout_len = 5;
-}
-
-/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */
-static inline void build_wide_nego_msg(struct esp *esp, int size)
-{
- esp->cur_msgout[0] = EXTENDED_MESSAGE;
- esp->cur_msgout[1] = 2;
- esp->cur_msgout[2] = EXTENDED_WDTR;
- switch (size) {
- case 32:
- esp->cur_msgout[3] = 2;
- break;
- case 16:
- esp->cur_msgout[3] = 1;
- break;
- case 8:
- default:
- esp->cur_msgout[3] = 0;
- break;
- };
-
- esp->msgout_len = 4;
-}
-
-static void esp_exec_cmd(struct esp *esp)
-{
- struct scsi_cmnd *SCptr;
- struct scsi_device *SDptr;
- struct esp_device *esp_dev;
- volatile u8 *cmdp = esp->esp_command;
- u8 the_esp_command;
- int lun, target;
- int i;
-
- /* Hold off if we have disconnected commands and
- * an IRQ is showing...
- */
- if (esp->disconnected_SC && ESP_IRQ_P(esp->dregs))
- return;
-
- /* Grab first member of the issue queue. */
- SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
-
- /* Safe to panic here because current_SC is null. */
- if (!SCptr)
- panic("esp: esp_exec_cmd and issue queue is NULL");
-
- SDptr = SCptr->device;
- esp_dev = SDptr->hostdata;
- lun = SCptr->device->lun;
- target = SCptr->device->id;
-
- esp->snip = 0;
- esp->msgout_len = 0;
-
- /* Send it out whole, or piece by piece? The ESP
- * only knows how to automatically send out 6, 10,
- * and 12 byte commands. I used to think that the
- * Linux SCSI code would never throw anything other
- * than that to us, but then again there is the
- * SCSI generic driver which can send us anything.
- */
- esp_check_cmd(esp, SCptr);
-
- /* If arbitration/selection is successful, the ESP will leave
- * ATN asserted, causing the target to go into message out
- * phase. The ESP will feed the target the identify and then
- * the target can only legally go to one of command,
- * datain/out, status, or message in phase, or stay in message
- * out phase (should we be trying to send a sync negotiation
- * message after the identify). It is not allowed to drop
- * BSY, but some buggy targets do and we check for this
- * condition in the selection complete code. Most of the time
- * we'll make the command bytes available to the ESP and it
- * will not interrupt us until it finishes command phase, we
- * cannot do this for command sizes the ESP does not
- * understand and in this case we'll get interrupted right
- * when the target goes into command phase.
- *
- * It is absolutely _illegal_ in the presence of SCSI-2 devices
- * to use the ESP select w/o ATN command. When SCSI-2 devices are
- * present on the bus we _must_ always go straight to message out
- * phase with an identify message for the target. Being that
- * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2
- * selections should not confuse SCSI-1 we hope.
- */
-
- if (esp_dev->sync) {
- /* this targets sync is known */
-#ifndef __sparc_v9__
-do_sync_known:
-#endif
- if (esp_dev->disconnect)
- *cmdp++ = IDENTIFY(1, lun);
- else
- *cmdp++ = IDENTIFY(0, lun);
-
- if (esp->esp_slowcmd) {
- the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
- esp_advance_phase(SCptr, in_slct_stop);
- } else {
- the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
- esp_advance_phase(SCptr, in_slct_norm);
- }
- } else if (!(esp->targets_present & (1<<target)) || !(esp_dev->disconnect)) {
- /* After the bootup SCSI code sends both the
- * TEST_UNIT_READY and INQUIRY commands we want
- * to at least attempt allowing the device to
- * disconnect.
- */
- ESPMISC(("esp: Selecting device for first time. target=%d "
- "lun=%d\n", target, SCptr->device->lun));
- if (!SDptr->borken && !esp_dev->disconnect)
- esp_dev->disconnect = 1;
-
- *cmdp++ = IDENTIFY(0, lun);
- esp->prevmsgout = NOP;
- esp_advance_phase(SCptr, in_slct_norm);
- the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
-
- /* Take no chances... */
- esp_dev->sync_max_offset = 0;
- esp_dev->sync_min_period = 0;
- } else {
- /* Sorry, I have had way too many problems with
- * various CDROM devices on ESP. -DaveM
- */
- int cdrom_hwbug_wkaround = 0;
-
-#ifndef __sparc_v9__
- /* Never allow disconnects or synchronous transfers on
- * SparcStation1 and SparcStation1+. Allowing those
- * to be enabled seems to lockup the machine completely.
- */
- if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
- (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
- /* But we are nice and allow tapes and removable
- * disks (but not CDROMs) to disconnect.
- */
- if(SDptr->type == TYPE_TAPE ||
- (SDptr->type != TYPE_ROM && SDptr->removable))
- esp_dev->disconnect = 1;
- else
- esp_dev->disconnect = 0;
- esp_dev->sync_max_offset = 0;
- esp_dev->sync_min_period = 0;
- esp_dev->sync = 1;
- esp->snip = 0;
- goto do_sync_known;
- }
-#endif /* !(__sparc_v9__) */
-
- /* We've talked to this guy before,
- * but never negotiated. Let's try,
- * need to attempt WIDE first, before
- * sync nego, as per SCSI 2 standard.
- */
- if (esp->erev == fashme && !esp_dev->wide) {
- if (!SDptr->borken &&
- SDptr->type != TYPE_ROM &&
- SDptr->removable == 0) {
- build_wide_nego_msg(esp, 16);
- esp_dev->wide = 1;
- esp->wnip = 1;
- goto after_nego_msg_built;
- } else {
- esp_dev->wide = 1;
- /* Fall through and try sync. */
- }
- }
-
- if (!SDptr->borken) {
- if ((SDptr->type == TYPE_ROM)) {
- /* Nice try sucker... */
- ESPMISC(("esp%d: Disabling sync for buggy "
- "CDROM.\n", esp->esp_id));
- cdrom_hwbug_wkaround = 1;
- build_sync_nego_msg(esp, 0, 0);
- } else if (SDptr->removable != 0) {
- ESPMISC(("esp%d: Not negotiating sync/wide but "
- "allowing disconnect for removable media.\n",
- esp->esp_id));
- build_sync_nego_msg(esp, 0, 0);
- } else {
- build_sync_nego_msg(esp, esp->sync_defp, 15);
- }
- } else {
- build_sync_nego_msg(esp, 0, 0);
- }
- esp_dev->sync = 1;
- esp->snip = 1;
-
-after_nego_msg_built:
- /* A fix for broken SCSI1 targets, when they disconnect
- * they lock up the bus and confuse ESP. So disallow
- * disconnects for SCSI1 targets for now until we
- * find a better fix.
- *
- * Addendum: This is funny, I figured out what was going
- * on. The blotzed SCSI1 target would disconnect,
- * one of the other SCSI2 targets or both would be
- * disconnected as well. The SCSI1 target would
- * stay disconnected long enough that we start
- * up a command on one of the SCSI2 targets. As
- * the ESP is arbitrating for the bus the SCSI1
- * target begins to arbitrate as well to reselect
- * the ESP. The SCSI1 target refuses to drop it's
- * ID bit on the data bus even though the ESP is
- * at ID 7 and is the obvious winner for any
- * arbitration. The ESP is a poor sport and refuses
- * to lose arbitration, it will continue indefinitely
- * trying to arbitrate for the bus and can only be
- * stopped via a chip reset or SCSI bus reset.
- * Therefore _no_ disconnects for SCSI1 targets
- * thank you very much. ;-)
- */
- if(((SDptr->scsi_level < 3) &&
- (SDptr->type != TYPE_TAPE) &&
- SDptr->removable == 0) ||
- cdrom_hwbug_wkaround || SDptr->borken) {
- ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d "
- "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun));
- esp_dev->disconnect = 0;
- *cmdp++ = IDENTIFY(0, lun);
- } else {
- *cmdp++ = IDENTIFY(1, lun);
- }
-
- /* ESP fifo is only so big...
- * Make this look like a slow command.
- */
- esp->esp_slowcmd = 1;
- esp->esp_scmdleft = SCptr->cmd_len;
- esp->esp_scmdp = &SCptr->cmnd[0];
-
- the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
- esp_advance_phase(SCptr, in_slct_msg);
- }
-
- if (!esp->esp_slowcmd)
- for (i = 0; i < SCptr->cmd_len; i++)
- *cmdp++ = SCptr->cmnd[i];
-
- /* HME sucks... */
- if (esp->erev == fashme)
- sbus_writeb((target & 0xf) | (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT),
- esp->eregs + ESP_BUSID);
- else
- sbus_writeb(target & 7, esp->eregs + ESP_BUSID);
- if (esp->prev_soff != esp_dev->sync_max_offset ||
- esp->prev_stp != esp_dev->sync_min_period ||
- (esp->erev > esp100a &&
- esp->prev_cfg3 != esp->config3[target])) {
- esp->prev_soff = esp_dev->sync_max_offset;
- esp->prev_stp = esp_dev->sync_min_period;
- sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
- sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
- if (esp->erev > esp100a) {
- esp->prev_cfg3 = esp->config3[target];
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
- }
- }
- i = (cmdp - esp->esp_command);
-
- if (esp->erev == fashme) {
- esp_cmd(esp, ESP_CMD_FLUSH); /* Grrr! */
-
- /* Set up the DMA and HME counters */
- sbus_writeb(i, esp->eregs + ESP_TCLOW);
- sbus_writeb(0, esp->eregs + ESP_TCMED);
- sbus_writeb(0, esp->eregs + FAS_RLO);
- sbus_writeb(0, esp->eregs + FAS_RHI);
- esp_cmd(esp, the_esp_command);
-
- /* Talk about touchy hardware... */
- esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
- (DMA_SCSI_DISAB | DMA_ENABLE)) &
- ~(DMA_ST_WRITE));
- sbus_writel(16, esp->dregs + DMA_COUNT);
- sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
- sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
- } else {
- u32 tmp;
-
- /* Set up the DMA and ESP counters */
- sbus_writeb(i, esp->eregs + ESP_TCLOW);
- sbus_writeb(0, esp->eregs + ESP_TCMED);
- tmp = sbus_readl(esp->dregs + DMA_CSR);
- tmp &= ~DMA_ST_WRITE;
- tmp |= DMA_ENABLE;
- sbus_writel(tmp, esp->dregs + DMA_CSR);
- if (esp->dma->revision == dvmaesc1) {
- if (i) /* Workaround ESC gate array SBUS rerun bug. */
- sbus_writel(PAGE_SIZE, esp->dregs + DMA_COUNT);
- }
- sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
-
- /* Tell ESP to "go". */
- esp_cmd(esp, the_esp_command);
- }
-}
-
-/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */
-static int esp_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
-{
- struct esp *esp;
-
- /* Set up func ptr and initial driver cmd-phase. */
- SCpnt->scsi_done = done;
- SCpnt->SCp.phase = not_issued;
-
- /* We use the scratch area. */
- ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->device->id, SCpnt->device->lun));
- ESPDISC(("N<%02x,%02x>", SCpnt->device->id, SCpnt->device->lun));
-
- esp = (struct esp *) SCpnt->device->host->hostdata;
- esp_get_dmabufs(esp, SCpnt);
- esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */
-
- SCpnt->SCp.Status = CHECK_CONDITION;
- SCpnt->SCp.Message = 0xff;
- SCpnt->SCp.sent_command = 0;
-
- /* Place into our queue. */
- if (SCpnt->cmnd[0] == REQUEST_SENSE) {
- ESPQUEUE(("RQSENSE\n"));
- prepend_SC(&esp->issue_SC, SCpnt);
- } else {
- ESPQUEUE(("\n"));
- append_SC(&esp->issue_SC, SCpnt);
- }
-
- /* Run it now if we can. */
- if (!esp->current_SC && !esp->resetting_bus)
- esp_exec_cmd(esp);
-
- return 0;
-}
-
-/* Dump driver state. */
-static void esp_dump_cmd(struct scsi_cmnd *SCptr)
-{
- ESPLOG(("[tgt<%02x> lun<%02x> "
- "pphase<%s> cphase<%s>]",
- SCptr->device->id, SCptr->device->lun,
- phase_string(SCptr->SCp.sent_command),
- phase_string(SCptr->SCp.phase)));
-}
-
-static void esp_dump_state(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
-#ifdef DEBUG_ESP_CMDS
- int i;
-#endif
-
- ESPLOG(("esp%d: dumping state\n", esp->esp_id));
- ESPLOG(("esp%d: dma -- cond_reg<%08x> addr<%08x>\n",
- esp->esp_id,
- sbus_readl(esp->dregs + DMA_CSR),
- sbus_readl(esp->dregs + DMA_ADDR)));
- ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
- esp->esp_id, esp->sreg, esp->seqreg, esp->ireg));
- ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
- esp->esp_id,
- sbus_readb(esp->eregs + ESP_STATUS),
- sbus_readb(esp->eregs + ESP_SSTEP),
- sbus_readb(esp->eregs + ESP_INTRPT)));
-#ifdef DEBUG_ESP_CMDS
- printk("esp%d: last ESP cmds [", esp->esp_id);
- i = (esp->espcmdent - 1) & 31;
- printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
- i = (i - 1) & 31;
- printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
- i = (i - 1) & 31;
- printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
- i = (i - 1) & 31;
- printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
- printk("]\n");
-#endif /* (DEBUG_ESP_CMDS) */
-
- if (SCptr) {
- ESPLOG(("esp%d: current command ", esp->esp_id));
- esp_dump_cmd(SCptr);
- }
- ESPLOG(("\n"));
- SCptr = esp->disconnected_SC;
- ESPLOG(("esp%d: disconnected ", esp->esp_id));
- while (SCptr) {
- esp_dump_cmd(SCptr);
- SCptr = (struct scsi_cmnd *) SCptr->host_scribble;
- }
- ESPLOG(("\n"));
-}
-
-/* Abort a command. The host_lock is acquired by caller. */
-static int esp_abort(struct scsi_cmnd *SCptr)
-{
- struct esp *esp = (struct esp *) SCptr->device->host->hostdata;
- int don;
-
- ESPLOG(("esp%d: Aborting command\n", esp->esp_id));
- esp_dump_state(esp);
-
- /* Wheee, if this is the current command on the bus, the
- * best we can do is assert ATN and wait for msgout phase.
- * This should even fix a hung SCSI bus when we lose state
- * in the driver and timeout because the eventual phase change
- * will cause the ESP to (eventually) give an interrupt.
- */
- if (esp->current_SC == SCptr) {
- esp->cur_msgout[0] = ABORT;
- esp->msgout_len = 1;
- esp->msgout_ctr = 0;
- esp_cmd(esp, ESP_CMD_SATN);
- return SUCCESS;
- }
-
- /* If it is still in the issue queue then we can safely
- * call the completion routine and report abort success.
- */
- don = (sbus_readl(esp->dregs + DMA_CSR) & DMA_INT_ENAB);
- if (don) {
- ESP_INTSOFF(esp->dregs);
- }
- if (esp->issue_SC) {
- struct scsi_cmnd **prev, *this;
- for (prev = (&esp->issue_SC), this = esp->issue_SC;
- this != NULL;
- prev = (struct scsi_cmnd **) &(this->host_scribble),
- this = (struct scsi_cmnd *) this->host_scribble) {
-
- if (this == SCptr) {
- *prev = (struct scsi_cmnd *) this->host_scribble;
- this->host_scribble = NULL;
-
- esp_release_dmabufs(esp, this);
- this->result = DID_ABORT << 16;
- this->scsi_done(this);
-
- if (don)
- ESP_INTSON(esp->dregs);
-
- return SUCCESS;
- }
- }
- }
-
- /* Yuck, the command to abort is disconnected, it is not
- * worth trying to abort it now if something else is live
- * on the bus at this time. So, we let the SCSI code wait
- * a little bit and try again later.
- */
- if (esp->current_SC) {
- if (don)
- ESP_INTSON(esp->dregs);
- return FAILED;
- }
-
- /* It's disconnected, we have to reconnect to re-establish
- * the nexus and tell the device to abort. However, we really
- * cannot 'reconnect' per se. Don't try to be fancy, just
- * indicate failure, which causes our caller to reset the whole
- * bus.
- */
-
- if (don)
- ESP_INTSON(esp->dregs);
-
- return FAILED;
-}
-
-/* We've sent ESP_CMD_RS to the ESP, the interrupt had just
- * arrived indicating the end of the SCSI bus reset. Our job
- * is to clean out the command queues and begin re-execution
- * of SCSI commands once more.
- */
-static int esp_finish_reset(struct esp *esp)
-{
- struct scsi_cmnd *sp = esp->current_SC;
-
- /* Clean up currently executing command, if any. */
- if (sp != NULL) {
- esp->current_SC = NULL;
-
- esp_release_dmabufs(esp, sp);
- sp->result = (DID_RESET << 16);
-
- sp->scsi_done(sp);
- }
-
- /* Clean up disconnected queue, they have been invalidated
- * by the bus reset.
- */
- if (esp->disconnected_SC) {
- while ((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) {
- esp_release_dmabufs(esp, sp);
- sp->result = (DID_RESET << 16);
-
- sp->scsi_done(sp);
- }
- }
-
- /* SCSI bus reset is complete. */
- esp->resetting_bus = 0;
- wake_up(&esp->reset_queue);
-
- /* Ok, now it is safe to get commands going once more. */
- if (esp->issue_SC)
- esp_exec_cmd(esp);
-
- return do_intr_end;
-}
-
-static int esp_do_resetbus(struct esp *esp)
-{
- ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
- esp->resetting_bus = 1;
- esp_cmd(esp, ESP_CMD_RS);
-
- return do_intr_end;
-}
-
-/* Reset ESP chip, reset hanging bus, then kill active and
- * disconnected commands for targets without soft reset.
- *
- * The host_lock is acquired by caller.
- */
-static int esp_reset(struct scsi_cmnd *SCptr)
-{
- struct esp *esp = (struct esp *) SCptr->device->host->hostdata;
-
- spin_lock_irq(esp->ehost->host_lock);
- (void) esp_do_resetbus(esp);
- spin_unlock_irq(esp->ehost->host_lock);
-
- wait_event(esp->reset_queue, (esp->resetting_bus == 0));
-
- return SUCCESS;
-}
-
-/* Internal ESP done function. */
-static void esp_done(struct esp *esp, int error)
-{
- struct scsi_cmnd *done_SC = esp->current_SC;
-
- esp->current_SC = NULL;
-
- esp_release_dmabufs(esp, done_SC);
- done_SC->result = error;
-
- done_SC->scsi_done(done_SC);
-
- /* Bus is free, issue any commands in the queue. */
- if (esp->issue_SC && !esp->current_SC)
- esp_exec_cmd(esp);
-
-}
-
-/* Wheee, ESP interrupt engine. */
-
-/* Forward declarations. */
-static int esp_do_phase_determine(struct esp *esp);
-static int esp_do_data_finale(struct esp *esp);
-static int esp_select_complete(struct esp *esp);
-static int esp_do_status(struct esp *esp);
-static int esp_do_msgin(struct esp *esp);
-static int esp_do_msgindone(struct esp *esp);
-static int esp_do_msgout(struct esp *esp);
-static int esp_do_cmdbegin(struct esp *esp);
-
-#define sreg_datainp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DIP)
-#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP)
-
-/* Read any bytes found in the FAS366 fifo, storing them into
- * the ESP driver software state structure.
- */
-static void hme_fifo_read(struct esp *esp)
-{
- u8 count = 0;
- u8 status = esp->sreg;
-
- /* Cannot safely frob the fifo for these following cases, but
- * we must always read the fifo when the reselect interrupt
- * is pending.
- */
- if (((esp->ireg & ESP_INTR_RSEL) == 0) &&
- (sreg_datainp(status) ||
- sreg_dataoutp(status) ||
- (esp->current_SC &&
- esp->current_SC->SCp.phase == in_data_done))) {
- ESPHME(("<wkaround_skipped>"));
- } else {
- unsigned long fcnt = sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES;
-
- /* The HME stores bytes in multiples of 2 in the fifo. */
- ESPHME(("hme_fifo[fcnt=%d", (int)fcnt));
- while (fcnt) {
- esp->hme_fifo_workaround_buffer[count++] =
- sbus_readb(esp->eregs + ESP_FDATA);
- esp->hme_fifo_workaround_buffer[count++] =
- sbus_readb(esp->eregs + ESP_FDATA);
- ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1]));
- fcnt--;
- }
- if (sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_F1BYTE) {
- ESPHME(("<poke_byte>"));
- sbus_writeb(0, esp->eregs + ESP_FDATA);
- esp->hme_fifo_workaround_buffer[count++] =
- sbus_readb(esp->eregs + ESP_FDATA);
- ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1]));
- ESPHME(("CMD_FLUSH"));
- esp_cmd(esp, ESP_CMD_FLUSH);
- } else {
- ESPHME(("no_xtra_byte"));
- }
- }
- ESPHME(("wkarnd_cnt=%d]", (int)count));
- esp->hme_fifo_workaround_count = count;
-}
-
-static inline void hme_fifo_push(struct esp *esp, u8 *bytes, u8 count)
-{
- esp_cmd(esp, ESP_CMD_FLUSH);
- while (count) {
- u8 tmp = *bytes++;
- sbus_writeb(tmp, esp->eregs + ESP_FDATA);
- sbus_writeb(0, esp->eregs + ESP_FDATA);
- count--;
- }
-}
-
-/* We try to avoid some interrupts by jumping ahead and see if the ESP
- * has gotten far enough yet. Hence the following.
- */
-static inline int skipahead1(struct esp *esp, struct scsi_cmnd *scp,
- int prev_phase, int new_phase)
-{
- if (scp->SCp.sent_command != prev_phase)
- return 0;
- if (ESP_IRQ_P(esp->dregs)) {
- /* Yes, we are able to save an interrupt. */
- if (esp->erev == fashme)
- esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
- esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR));
- esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
- if (esp->erev == fashme) {
- /* This chip is really losing. */
- ESPHME(("HME["));
- /* Must latch fifo before reading the interrupt
- * register else garbage ends up in the FIFO
- * which confuses the driver utterly.
- * Happy Meal indeed....
- */
- ESPHME(("fifo_workaround]"));
- if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
- (esp->sreg2 & ESP_STAT2_F1BYTE))
- hme_fifo_read(esp);
- }
- if (!(esp->ireg & ESP_INTR_SR))
- return 0;
- else
- return do_reset_complete;
- }
- /* Ho hum, target is taking forever... */
- scp->SCp.sent_command = new_phase; /* so we don't recurse... */
- return do_intr_end;
-}
-
-static inline int skipahead2(struct esp *esp, struct scsi_cmnd *scp,
- int prev_phase1, int prev_phase2, int new_phase)
-{
- if (scp->SCp.sent_command != prev_phase1 &&
- scp->SCp.sent_command != prev_phase2)
- return 0;
- if (ESP_IRQ_P(esp->dregs)) {
- /* Yes, we are able to save an interrupt. */
- if (esp->erev == fashme)
- esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
- esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR));
- esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
- if (esp->erev == fashme) {
- /* This chip is really losing. */
- ESPHME(("HME["));
-
- /* Must latch fifo before reading the interrupt
- * register else garbage ends up in the FIFO
- * which confuses the driver utterly.
- * Happy Meal indeed....
- */
- ESPHME(("fifo_workaround]"));
- if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
- (esp->sreg2 & ESP_STAT2_F1BYTE))
- hme_fifo_read(esp);
- }
- if (!(esp->ireg & ESP_INTR_SR))
- return 0;
- else
- return do_reset_complete;
- }
- /* Ho hum, target is taking forever... */
- scp->SCp.sent_command = new_phase; /* so we don't recurse... */
- return do_intr_end;
-}
-
-/* Now some dma helpers. */
-static void dma_setup(struct esp *esp, __u32 addr, int count, int write)
-{
- u32 nreg = sbus_readl(esp->dregs + DMA_CSR);
-
- if (write)
- nreg |= DMA_ST_WRITE;
- else
- nreg &= ~(DMA_ST_WRITE);
- nreg |= DMA_ENABLE;
- sbus_writel(nreg, esp->dregs + DMA_CSR);
- if (esp->dma->revision == dvmaesc1) {
- /* This ESC gate array sucks! */
- __u32 src = addr;
- __u32 dest = src + count;
-
- if (dest & (PAGE_SIZE - 1))
- count = PAGE_ALIGN(count);
- sbus_writel(count, esp->dregs + DMA_COUNT);
- }
- sbus_writel(addr, esp->dregs + DMA_ADDR);
-}
-
-static void dma_drain(struct esp *esp)
-{
- u32 tmp;
-
- if (esp->dma->revision == dvmahme)
- return;
- if ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_FIFO_ISDRAIN) {
- switch (esp->dma->revision) {
- default:
- tmp |= DMA_FIFO_STDRAIN;
- sbus_writel(tmp, esp->dregs + DMA_CSR);
-
- case dvmarev3:
- case dvmaesc1:
- while (sbus_readl(esp->dregs + DMA_CSR) & DMA_FIFO_ISDRAIN)
- udelay(1);
- };
- }
-}
-
-static void dma_invalidate(struct esp *esp)
-{
- u32 tmp;
-
- if (esp->dma->revision == dvmahme) {
- sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR);
-
- esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
- (DMA_PARITY_OFF | DMA_2CLKS |
- DMA_SCSI_DISAB | DMA_INT_ENAB)) &
- ~(DMA_ST_WRITE | DMA_ENABLE));
-
- sbus_writel(0, esp->dregs + DMA_CSR);
- sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
-
- /* This is necessary to avoid having the SCSI channel
- * engine lock up on us.
- */
- sbus_writel(0, esp->dregs + DMA_ADDR);
- } else {
- while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ)
- udelay(1);
-
- tmp &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
- tmp |= DMA_FIFO_INV;
- sbus_writel(tmp, esp->dregs + DMA_CSR);
- tmp &= ~DMA_FIFO_INV;
- sbus_writel(tmp, esp->dregs + DMA_CSR);
- }
-}
-
-static inline void dma_flashclear(struct esp *esp)
-{
- dma_drain(esp);
- dma_invalidate(esp);
-}
-
-static int dma_can_transfer(struct esp *esp, struct scsi_cmnd *sp)
-{
- __u32 base, end, sz;
-
- if (esp->dma->revision == dvmarev3) {
- sz = sp->SCp.this_residual;
- if (sz > 0x1000000)
- sz = 0x1000000;
- } else {
- base = ((__u32)((unsigned long)sp->SCp.ptr));
- base &= (0x1000000 - 1);
- end = (base + sp->SCp.this_residual);
- if (end > 0x1000000)
- end = 0x1000000;
- sz = (end - base);
- }
- return sz;
-}
-
-/* Misc. esp helper macros. */
-#define esp_setcount(__eregs, __cnt, __hme) \
- sbus_writeb(((__cnt)&0xff), (__eregs) + ESP_TCLOW); \
- sbus_writeb((((__cnt)>>8)&0xff), (__eregs) + ESP_TCMED); \
- if (__hme) { \
- sbus_writeb((((__cnt)>>16)&0xff), (__eregs) + FAS_RLO); \
- sbus_writeb(0, (__eregs) + FAS_RHI); \
- }
-
-#define esp_getcount(__eregs, __hme) \
- ((sbus_readb((__eregs) + ESP_TCLOW)&0xff) | \
- ((sbus_readb((__eregs) + ESP_TCMED)&0xff) << 8) | \
- ((__hme) ? sbus_readb((__eregs) + FAS_RLO) << 16 : 0))
-
-#define fcount(__esp) \
- (((__esp)->erev == fashme) ? \
- (__esp)->hme_fifo_workaround_count : \
- sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_FBYTES)
-
-#define fnzero(__esp) \
- (((__esp)->erev == fashme) ? 0 : \
- sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_ONOTZERO)
-
-/* XXX speculative nops unnecessary when continuing amidst a data phase
- * XXX even on esp100!!! another case of flooding the bus with I/O reg
- * XXX writes...
- */
-#define esp_maybe_nop(__esp) \
- if ((__esp)->erev == esp100) \
- esp_cmd((__esp), ESP_CMD_NULL)
-
-#define sreg_to_dataphase(__sreg) \
- ((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain)
-
-/* The ESP100 when in synchronous data phase, can mistake a long final
- * REQ pulse from the target as an extra byte, it places whatever is on
- * the data lines into the fifo. For now, we will assume when this
- * happens that the target is a bit quirky and we don't want to
- * be talking synchronously to it anyways. Regardless, we need to
- * tell the ESP to eat the extraneous byte so that we can proceed
- * to the next phase.
- */
-static int esp100_sync_hwbug(struct esp *esp, struct scsi_cmnd *sp, int fifocnt)
-{
- /* Do not touch this piece of code. */
- if ((!(esp->erev == esp100)) ||
- (!(sreg_datainp((esp->sreg = sbus_readb(esp->eregs + ESP_STATUS))) &&
- !fifocnt) &&
- !(sreg_dataoutp(esp->sreg) && !fnzero(esp)))) {
- if (sp->SCp.phase == in_dataout)
- esp_cmd(esp, ESP_CMD_FLUSH);
- return 0;
- } else {
- /* Async mode for this guy. */
- build_sync_nego_msg(esp, 0, 0);
-
- /* Ack the bogus byte, but set ATN first. */
- esp_cmd(esp, ESP_CMD_SATN);
- esp_cmd(esp, ESP_CMD_MOK);
- return 1;
- }
-}
-
-/* This closes the window during a selection with a reselect pending, because
- * we use DMA for the selection process the FIFO should hold the correct
- * contents if we get reselected during this process. So we just need to
- * ack the possible illegal cmd interrupt pending on the esp100.
- */
-static inline int esp100_reconnect_hwbug(struct esp *esp)
-{
- u8 tmp;
-
- if (esp->erev != esp100)
- return 0;
- tmp = sbus_readb(esp->eregs + ESP_INTRPT);
- if (tmp & ESP_INTR_SR)
- return 1;
- return 0;
-}
-
-/* This verifies the BUSID bits during a reselection so that we know which
- * target is talking to us.
- */
-static inline int reconnect_target(struct esp *esp)
-{
- int it, me = esp->scsi_id_mask, targ = 0;
-
- if (2 != fcount(esp))
- return -1;
- if (esp->erev == fashme) {
- /* HME does not latch it's own BUS ID bits during
- * a reselection. Also the target number is given
- * as an unsigned char, not as a sole bit number
- * like the other ESP's do.
- * Happy Meal indeed....
- */
- targ = esp->hme_fifo_workaround_buffer[0];
- } else {
- it = sbus_readb(esp->eregs + ESP_FDATA);
- if (!(it & me))
- return -1;
- it &= ~me;
- if (it & (it - 1))
- return -1;
- while (!(it & 1))
- targ++, it >>= 1;
- }
- return targ;
-}
-
-/* This verifies the identify from the target so that we know which lun is
- * being reconnected.
- */
-static inline int reconnect_lun(struct esp *esp)
-{
- int lun;
-
- if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP)
- return -1;
- if (esp->erev == fashme)
- lun = esp->hme_fifo_workaround_buffer[1];
- else
- lun = sbus_readb(esp->eregs + ESP_FDATA);
-
- /* Yes, you read this correctly. We report lun of zero
- * if we see parity error. ESP reports parity error for
- * the lun byte, and this is the only way to hope to recover
- * because the target is connected.
- */
- if (esp->sreg & ESP_STAT_PERR)
- return 0;
-
- /* Check for illegal bits being set in the lun. */
- if ((lun & 0x40) || !(lun & 0x80))
- return -1;
-
- return lun & 7;
-}
-
-/* This puts the driver in a state where it can revitalize a command that
- * is being continued due to reselection.
- */
-static inline void esp_connect(struct esp *esp, struct scsi_cmnd *sp)
-{
- struct esp_device *esp_dev = sp->device->hostdata;
-
- if (esp->prev_soff != esp_dev->sync_max_offset ||
- esp->prev_stp != esp_dev->sync_min_period ||
- (esp->erev > esp100a &&
- esp->prev_cfg3 != esp->config3[sp->device->id])) {
- esp->prev_soff = esp_dev->sync_max_offset;
- esp->prev_stp = esp_dev->sync_min_period;
- sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
- sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
- if (esp->erev > esp100a) {
- esp->prev_cfg3 = esp->config3[sp->device->id];
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
- }
- }
- esp->current_SC = sp;
-}
-
-/* This will place the current working command back into the issue queue
- * if we are to receive a reselection amidst a selection attempt.
- */
-static inline void esp_reconnect(struct esp *esp, struct scsi_cmnd *sp)
-{
- if (!esp->disconnected_SC)
- ESPLOG(("esp%d: Weird, being reselected but disconnected "
- "command queue is empty.\n", esp->esp_id));
- esp->snip = 0;
- esp->current_SC = NULL;
- sp->SCp.phase = not_issued;
- append_SC(&esp->issue_SC, sp);
-}
-
-/* Begin message in phase. */
-static int esp_do_msgin(struct esp *esp)
-{
- /* Must be very careful with the fifo on the HME */
- if ((esp->erev != fashme) ||
- !(sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_FEMPTY))
- esp_cmd(esp, ESP_CMD_FLUSH);
- esp_maybe_nop(esp);
- esp_cmd(esp, ESP_CMD_TI);
- esp->msgin_len = 1;
- esp->msgin_ctr = 0;
- esp_advance_phase(esp->current_SC, in_msgindone);
- return do_work_bus;
-}
-
-/* This uses various DMA csr fields and the fifo flags count value to
- * determine how many bytes were successfully sent/received by the ESP.
- */
-static inline int esp_bytes_sent(struct esp *esp, int fifo_count)
-{
- int rval = sbus_readl(esp->dregs + DMA_ADDR) - esp->esp_command_dvma;
-
- if (esp->dma->revision == dvmarev1)
- rval -= (4 - ((sbus_readl(esp->dregs + DMA_CSR) & DMA_READ_AHEAD)>>11));
- return rval - fifo_count;
-}
-
-static inline void advance_sg(struct scsi_cmnd *sp)
-{
- ++sp->SCp.buffer;
- --sp->SCp.buffers_residual;
- sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer);
- sp->SCp.ptr = (char *)((unsigned long)sg_dma_address(sp->SCp.buffer));
-}
-
-/* Please note that the way I've coded these routines is that I _always_
- * check for a disconnect during any and all information transfer
- * phases. The SCSI standard states that the target _can_ cause a BUS
- * FREE condition by dropping all MSG/CD/IO/BSY signals. Also note
- * that during information transfer phases the target controls every
- * change in phase, the only thing the initiator can do is "ask" for
- * a message out phase by driving ATN true. The target can, and sometimes
- * will, completely ignore this request so we cannot assume anything when
- * we try to force a message out phase to abort/reset a target. Most of
- * the time the target will eventually be nice and go to message out, so
- * we may have to hold on to our state about what we want to tell the target
- * for some period of time.
- */
-
-/* I think I have things working here correctly. Even partial transfers
- * within a buffer or sub-buffer should not upset us at all no matter
- * how bad the target and/or ESP fucks things up.
- */
-static int esp_do_data(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
- int thisphase, hmuch;
-
- ESPDATA(("esp_do_data: "));
- esp_maybe_nop(esp);
- thisphase = sreg_to_dataphase(esp->sreg);
- esp_advance_phase(SCptr, thisphase);
- ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT"));
- hmuch = dma_can_transfer(esp, SCptr);
- if (hmuch > (64 * 1024) && (esp->erev != fashme))
- hmuch = (64 * 1024);
- ESPDATA(("hmuch<%d> ", hmuch));
- esp->current_transfer_size = hmuch;
-
- if (esp->erev == fashme) {
- u32 tmp = esp->prev_hme_dmacsr;
-
- /* Always set the ESP count registers first. */
- esp_setcount(esp->eregs, hmuch, 1);
-
- /* Get the DMA csr computed. */
- tmp |= (DMA_SCSI_DISAB | DMA_ENABLE);
- if (thisphase == in_datain)
- tmp |= DMA_ST_WRITE;
- else
- tmp &= ~(DMA_ST_WRITE);
- esp->prev_hme_dmacsr = tmp;
-
- ESPDATA(("DMA|TI --> do_intr_end\n"));
- if (thisphase == in_datain) {
- sbus_writel(hmuch, esp->dregs + DMA_COUNT);
- esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
- } else {
- esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
- sbus_writel(hmuch, esp->dregs + DMA_COUNT);
- }
- sbus_writel((__u32)((unsigned long)SCptr->SCp.ptr), esp->dregs+DMA_ADDR);
- sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
- } else {
- esp_setcount(esp->eregs, hmuch, 0);
- dma_setup(esp, ((__u32)((unsigned long)SCptr->SCp.ptr)),
- hmuch, (thisphase == in_datain));
- ESPDATA(("DMA|TI --> do_intr_end\n"));
- esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
- }
- return do_intr_end;
-}
-
-/* See how successful the data transfer was. */
-static int esp_do_data_finale(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
- struct esp_device *esp_dev = SCptr->device->hostdata;
- int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0;
-
- ESPDATA(("esp_do_data_finale: "));
-
- if (SCptr->SCp.phase == in_datain) {
- if (esp->sreg & ESP_STAT_PERR) {
- /* Yuck, parity error. The ESP asserts ATN
- * so that we can go to message out phase
- * immediately and inform the target that
- * something bad happened.
- */
- ESPLOG(("esp%d: data bad parity detected.\n",
- esp->esp_id));
- esp->cur_msgout[0] = INITIATOR_ERROR;
- esp->msgout_len = 1;
- }
- dma_drain(esp);
- }
- dma_invalidate(esp);
-
- /* This could happen for the above parity error case. */
- if (esp->ireg != ESP_INTR_BSERV) {
- /* Please go to msgout phase, please please please... */
- ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",
- esp->esp_id));
- return esp_do_phase_determine(esp);
- }
-
- /* Check for partial transfers and other horrible events.
- * Note, here we read the real fifo flags register even
- * on HME broken adapters because we skip the HME fifo
- * workaround code in esp_handle() if we are doing data
- * phase things. We don't want to fuck directly with
- * the fifo like that, especially if doing synchronous
- * transfers! Also, will need to double the count on
- * HME if we are doing wide transfers, as the HME fifo
- * will move and count 16-bit quantities during wide data.
- * SMCC _and_ Qlogic can both bite me.
- */
- fifocnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES);
- if (esp->erev != fashme)
- ecount = esp_getcount(esp->eregs, 0);
- bytes_sent = esp->current_transfer_size;
-
- ESPDATA(("trans_sz(%d), ", bytes_sent));
- if (esp->erev == fashme) {
- if (!(esp->sreg & ESP_STAT_TCNT)) {
- ecount = esp_getcount(esp->eregs, 1);
- bytes_sent -= ecount;
- }
-
- /* Always subtract any cruft remaining in the FIFO. */
- if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE)
- fifocnt <<= 1;
- if (SCptr->SCp.phase == in_dataout)
- bytes_sent -= fifocnt;
-
- /* I have an IBM disk which exhibits the following
- * behavior during writes to it. It disconnects in
- * the middle of a partial transfer, the current sglist
- * buffer is 1024 bytes, the disk stops data transfer
- * at 512 bytes.
- *
- * However the FAS366 reports that 32 more bytes were
- * transferred than really were. This is precisely
- * the size of a fully loaded FIFO in wide scsi mode.
- * The FIFO state recorded indicates that it is empty.
- *
- * I have no idea if this is a bug in the FAS366 chip
- * or a bug in the firmware on this IBM disk. In any
- * event the following seems to be a good workaround. -DaveM
- */
- if (bytes_sent != esp->current_transfer_size &&
- SCptr->SCp.phase == in_dataout) {
- int mask = (64 - 1);
-
- if ((esp->prev_cfg3 & ESP_CONFIG3_EWIDE) == 0)
- mask >>= 1;
-
- if (bytes_sent & mask)
- bytes_sent -= (bytes_sent & mask);
- }
- } else {
- if (!(esp->sreg & ESP_STAT_TCNT))
- bytes_sent -= ecount;
- if (SCptr->SCp.phase == in_dataout)
- bytes_sent -= fifocnt;
- }
-
- ESPDATA(("bytes_sent(%d), ", bytes_sent));
-
- /* If we were in synchronous mode, check for peculiarities. */
- if (esp->erev == fashme) {
- if (esp_dev->sync_max_offset) {
- if (SCptr->SCp.phase == in_dataout)
- esp_cmd(esp, ESP_CMD_FLUSH);
- } else {
- esp_cmd(esp, ESP_CMD_FLUSH);
- }
- } else {
- if (esp_dev->sync_max_offset)
- bogus_data = esp100_sync_hwbug(esp, SCptr, fifocnt);
- else
- esp_cmd(esp, ESP_CMD_FLUSH);
- }
-
- /* Until we are sure of what has happened, we are certainly
- * in the dark.
- */
- esp_advance_phase(SCptr, in_the_dark);
-
- if (bytes_sent < 0) {
- /* I've seen this happen due to lost state in this
- * driver. No idea why it happened, but allowing
- * this value to be negative caused things to
- * lock up. This allows greater chance of recovery.
- * In fact every time I've seen this, it has been
- * a driver bug without question.
- */
- ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));
- ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",
- esp->esp_id,
- esp->current_transfer_size, fifocnt, ecount));
- ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",
- esp->esp_id,
- SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));
- ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id,
- SCptr->device->id));
- SCptr->device->borken = 1;
- esp_dev->sync = 0;
- bytes_sent = 0;
- }
-
- /* Update the state of our transfer. */
- SCptr->SCp.ptr += bytes_sent;
- SCptr->SCp.this_residual -= bytes_sent;
- if (SCptr->SCp.this_residual < 0) {
- /* shit */
- ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id));
- SCptr->SCp.this_residual = 0;
- }
-
- /* Maybe continue. */
- if (!bogus_data) {
- ESPDATA(("!bogus_data, "));
-
- /* NO MATTER WHAT, we advance the scatterlist,
- * if the target should decide to disconnect
- * in between scatter chunks (which is common)
- * we could die horribly! I used to have the sg
- * advance occur only if we are going back into
- * (or are staying in) a data phase, you can
- * imagine the hell I went through trying to
- * figure this out.
- */
- if (SCptr->use_sg && !SCptr->SCp.this_residual)
- advance_sg(SCptr);
- if (sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) {
- ESPDATA(("to more data\n"));
- return esp_do_data(esp);
- }
- ESPDATA(("to new phase\n"));
- return esp_do_phase_determine(esp);
- }
- /* Bogus data, just wait for next interrupt. */
- ESPLOG(("esp%d: bogus_data during end of data phase\n",
- esp->esp_id));
- return do_intr_end;
-}
-
-/* We received a non-good status return at the end of
- * running a SCSI command. This is used to decide if
- * we should clear our synchronous transfer state for
- * such a device when that happens.
- *
- * The idea is that when spinning up a disk or rewinding
- * a tape, we don't want to go into a loop re-negotiating
- * synchronous capabilities over and over.
- */
-static int esp_should_clear_sync(struct scsi_cmnd *sp)
-{
- u8 cmd = sp->cmnd[0];
-
- /* These cases are for spinning up a disk and
- * waiting for that spinup to complete.
- */
- if (cmd == START_STOP)
- return 0;
-
- if (cmd == TEST_UNIT_READY)
- return 0;
-
- /* One more special case for SCSI tape drives,
- * this is what is used to probe the device for
- * completion of a rewind or tape load operation.
- */
- if (sp->device->type == TYPE_TAPE) {
- if (cmd == MODE_SENSE)
- return 0;
- }
-
- return 1;
-}
-
-/* Either a command is completing or a target is dropping off the bus
- * to continue the command in the background so we can do other work.
- */
-static int esp_do_freebus(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
- struct esp_device *esp_dev = SCptr->device->hostdata;
- int rval;
-
- rval = skipahead2(esp, SCptr, in_status, in_msgindone, in_freeing);
- if (rval)
- return rval;
- if (esp->ireg != ESP_INTR_DC) {
- ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id));
- return do_reset_bus; /* target will not drop BSY... */
- }
- esp->msgout_len = 0;
- esp->prevmsgout = NOP;
- if (esp->prevmsgin == COMMAND_COMPLETE) {
- /* Normal end of nexus. */
- if (esp->disconnected_SC || (esp->erev == fashme))
- esp_cmd(esp, ESP_CMD_ESEL);
-
- if (SCptr->SCp.Status != GOOD &&
- SCptr->SCp.Status != CONDITION_GOOD &&
- ((1<<SCptr->device->id) & esp->targets_present) &&
- esp_dev->sync &&
- esp_dev->sync_max_offset) {
- /* SCSI standard says that the synchronous capabilities
- * should be renegotiated at this point. Most likely
- * we are about to request sense from this target
- * in which case we want to avoid using sync
- * transfers until we are sure of the current target
- * state.
- */
- ESPMISC(("esp: Status <%d> for target %d lun %d\n",
- SCptr->SCp.Status, SCptr->device->id, SCptr->device->lun));
-
- /* But don't do this when spinning up a disk at
- * boot time while we poll for completion as it
- * fills up the console with messages. Also, tapes
- * can report not ready many times right after
- * loading up a tape.
- */
- if (esp_should_clear_sync(SCptr) != 0)
- esp_dev->sync = 0;
- }
- ESPDISC(("F<%02x,%02x>", SCptr->device->id, SCptr->device->lun));
- esp_done(esp, ((SCptr->SCp.Status & 0xff) |
- ((SCptr->SCp.Message & 0xff)<<8) |
- (DID_OK << 16)));
- } else if (esp->prevmsgin == DISCONNECT) {
- /* Normal disconnect. */
- esp_cmd(esp, ESP_CMD_ESEL);
- ESPDISC(("D<%02x,%02x>", SCptr->device->id, SCptr->device->lun));
- append_SC(&esp->disconnected_SC, SCptr);
- esp->current_SC = NULL;
- if (esp->issue_SC)
- esp_exec_cmd(esp);
- } else {
- /* Driver bug, we do not expect a disconnect here
- * and should not have advanced the state engine
- * to in_freeing.
- */
- ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n",
- esp->esp_id));
- return do_reset_bus;
- }
- return do_intr_end;
-}
-
-/* When a reselect occurs, and we cannot find the command to
- * reconnect to in our queues, we do this.
- */
-static int esp_bad_reconnect(struct esp *esp)
-{
- struct scsi_cmnd *sp;
-
- ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
- esp->esp_id));
- ESPLOG(("QUEUE DUMP\n"));
- sp = esp->issue_SC;
- ESPLOG(("esp%d: issue_SC[", esp->esp_id));
- while (sp) {
- ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
- sp = (struct scsi_cmnd *) sp->host_scribble;
- }
- ESPLOG(("]\n"));
- sp = esp->current_SC;
- ESPLOG(("esp%d: current_SC[", esp->esp_id));
- if (sp)
- ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
- else
- ESPLOG(("<NULL>"));
- ESPLOG(("]\n"));
- sp = esp->disconnected_SC;
- ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
- while (sp) {
- ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
- sp = (struct scsi_cmnd *) sp->host_scribble;
- }
- ESPLOG(("]\n"));
- return do_reset_bus;
-}
-
-/* Do the needy when a target tries to reconnect to us. */
-static int esp_do_reconnect(struct esp *esp)
-{
- int lun, target;
- struct scsi_cmnd *SCptr;
-
- /* Check for all bogus conditions first. */
- target = reconnect_target(esp);
- if (target < 0) {
- ESPDISC(("bad bus bits\n"));
- return do_reset_bus;
- }
- lun = reconnect_lun(esp);
- if (lun < 0) {
- ESPDISC(("target=%2x, bad identify msg\n", target));
- return do_reset_bus;
- }
-
- /* Things look ok... */
- ESPDISC(("R<%02x,%02x>", target, lun));
-
- /* Must not flush FIFO or DVMA on HME. */
- if (esp->erev != fashme) {
- esp_cmd(esp, ESP_CMD_FLUSH);
- if (esp100_reconnect_hwbug(esp))
- return do_reset_bus;
- esp_cmd(esp, ESP_CMD_NULL);
- }
-
- SCptr = remove_SC(&esp->disconnected_SC, (u8) target, (u8) lun);
- if (!SCptr)
- return esp_bad_reconnect(esp);
-
- esp_connect(esp, SCptr);
- esp_cmd(esp, ESP_CMD_MOK);
-
- if (esp->erev == fashme)
- sbus_writeb(((SCptr->device->id & 0xf) |
- (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT)),
- esp->eregs + ESP_BUSID);
-
- /* Reconnect implies a restore pointers operation. */
- esp_restore_pointers(esp, SCptr);
-
- esp->snip = 0;
- esp_advance_phase(SCptr, in_the_dark);
- return do_intr_end;
-}
-
-/* End of NEXUS (hopefully), pick up status + message byte then leave if
- * all goes well.
- */
-static int esp_do_status(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
- int intr, rval;
-
- rval = skipahead1(esp, SCptr, in_the_dark, in_status);
- if (rval)
- return rval;
- intr = esp->ireg;
- ESPSTAT(("esp_do_status: "));
- if (intr != ESP_INTR_DC) {
- int message_out = 0; /* for parity problems */
-
- /* Ack the message. */
- ESPSTAT(("ack msg, "));
- esp_cmd(esp, ESP_CMD_MOK);
-
- if (esp->erev != fashme) {
- dma_flashclear(esp);
-
- /* Wait till the first bits settle. */
- while (esp->esp_command[0] == 0xff)
- udelay(1);
- } else {
- esp->esp_command[0] = esp->hme_fifo_workaround_buffer[0];
- esp->esp_command[1] = esp->hme_fifo_workaround_buffer[1];
- }
-
- ESPSTAT(("got something, "));
- /* ESP chimes in with one of
- *
- * 1) function done interrupt:
- * both status and message in bytes
- * are available
- *
- * 2) bus service interrupt:
- * only status byte was acquired
- *
- * 3) Anything else:
- * can't happen, but we test for it
- * anyways
- *
- * ALSO: If bad parity was detected on either
- * the status _or_ the message byte then
- * the ESP has asserted ATN on the bus
- * and we must therefore wait for the
- * next phase change.
- */
- if (intr & ESP_INTR_FDONE) {
- /* We got it all, hallejulia. */
- ESPSTAT(("got both, "));
- SCptr->SCp.Status = esp->esp_command[0];
- SCptr->SCp.Message = esp->esp_command[1];
- esp->prevmsgin = SCptr->SCp.Message;
- esp->cur_msgin[0] = SCptr->SCp.Message;
- if (esp->sreg & ESP_STAT_PERR) {
- /* There was bad parity for the
- * message byte, the status byte
- * was ok.
- */
- message_out = MSG_PARITY_ERROR;
- }
- } else if (intr == ESP_INTR_BSERV) {
- /* Only got status byte. */
- ESPLOG(("esp%d: got status only, ", esp->esp_id));
- if (!(esp->sreg & ESP_STAT_PERR)) {
- SCptr->SCp.Status = esp->esp_command[0];
- SCptr->SCp.Message = 0xff;
- } else {
- /* The status byte had bad parity.
- * we leave the scsi_pointer Status
- * field alone as we set it to a default
- * of CHECK_CONDITION in esp_queue.
- */
- message_out = INITIATOR_ERROR;
- }
- } else {
- /* This shouldn't happen ever. */
- ESPSTAT(("got bolixed\n"));
- esp_advance_phase(SCptr, in_the_dark);
- return esp_do_phase_determine(esp);
- }
-
- if (!message_out) {
- ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status,
- SCptr->SCp.Message));
- if (SCptr->SCp.Message == COMMAND_COMPLETE) {
- ESPSTAT(("and was COMMAND_COMPLETE\n"));
- esp_advance_phase(SCptr, in_freeing);
- return esp_do_freebus(esp);
- } else {
- ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n",
- esp->esp_id));
- esp->msgin_len = esp->msgin_ctr = 1;
- esp_advance_phase(SCptr, in_msgindone);
- return esp_do_msgindone(esp);
- }
- } else {
- /* With luck we'll be able to let the target
- * know that bad parity happened, it will know
- * which byte caused the problems and send it
- * again. For the case where the status byte
- * receives bad parity, I do not believe most
- * targets recover very well. We'll see.
- */
- ESPLOG(("esp%d: bad parity somewhere mout=%2x\n",
- esp->esp_id, message_out));
- esp->cur_msgout[0] = message_out;
- esp->msgout_len = esp->msgout_ctr = 1;
- esp_advance_phase(SCptr, in_the_dark);
- return esp_do_phase_determine(esp);
- }
- } else {
- /* If we disconnect now, all hell breaks loose. */
- ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id));
- esp_advance_phase(SCptr, in_the_dark);
- return esp_do_phase_determine(esp);
- }
-}
-
-static int esp_enter_status(struct esp *esp)
-{
- u8 thecmd = ESP_CMD_ICCSEQ;
-
- esp_cmd(esp, ESP_CMD_FLUSH);
- if (esp->erev != fashme) {
- u32 tmp;
-
- esp->esp_command[0] = esp->esp_command[1] = 0xff;
- sbus_writeb(2, esp->eregs + ESP_TCLOW);
- sbus_writeb(0, esp->eregs + ESP_TCMED);
- tmp = sbus_readl(esp->dregs + DMA_CSR);
- tmp |= (DMA_ST_WRITE | DMA_ENABLE);
- sbus_writel(tmp, esp->dregs + DMA_CSR);
- if (esp->dma->revision == dvmaesc1)
- sbus_writel(0x100, esp->dregs + DMA_COUNT);
- sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
- thecmd |= ESP_CMD_DMA;
- }
- esp_cmd(esp, thecmd);
- esp_advance_phase(esp->current_SC, in_status);
-
- return esp_do_status(esp);
-}
-
-static int esp_disconnect_amidst_phases(struct esp *esp)
-{
- struct scsi_cmnd *sp = esp->current_SC;
- struct esp_device *esp_dev = sp->device->hostdata;
-
- /* This means real problems if we see this
- * here. Unless we were actually trying
- * to force the device to abort/reset.
- */
- ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id));
- ESPLOG(("pphase<%s> cphase<%s>, ",
- phase_string(sp->SCp.phase),
- phase_string(sp->SCp.sent_command)));
-
- if (esp->disconnected_SC != NULL || (esp->erev == fashme))
- esp_cmd(esp, ESP_CMD_ESEL);
-
- switch (esp->cur_msgout[0]) {
- default:
- /* We didn't expect this to happen at all. */
- ESPLOG(("device is bolixed\n"));
- esp_advance_phase(sp, in_tgterror);
- esp_done(esp, (DID_ERROR << 16));
- break;
-
- case BUS_DEVICE_RESET:
- ESPLOG(("device reset successful\n"));
- esp_dev->sync_max_offset = 0;
- esp_dev->sync_min_period = 0;
- esp_dev->sync = 0;
- esp_advance_phase(sp, in_resetdev);
- esp_done(esp, (DID_RESET << 16));
- break;
-
- case ABORT:
- ESPLOG(("device abort successful\n"));
- esp_advance_phase(sp, in_abortone);
- esp_done(esp, (DID_ABORT << 16));
- break;
-
- };
- return do_intr_end;
-}
-
-static int esp_enter_msgout(struct esp *esp)
-{
- esp_advance_phase(esp->current_SC, in_msgout);
- return esp_do_msgout(esp);
-}
-
-static int esp_enter_msgin(struct esp *esp)
-{
- esp_advance_phase(esp->current_SC, in_msgin);
- return esp_do_msgin(esp);
-}
-
-static int esp_enter_cmd(struct esp *esp)
-{
- esp_advance_phase(esp->current_SC, in_cmdbegin);
- return esp_do_cmdbegin(esp);
-}
-
-static int esp_enter_badphase(struct esp *esp)
-{
- ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
- esp->sreg & ESP_STAT_PMASK));
- return do_reset_bus;
-}
-
-typedef int (*espfunc_t)(struct esp *);
-
-static espfunc_t phase_vector[] = {
- esp_do_data, /* ESP_DOP */
- esp_do_data, /* ESP_DIP */
- esp_enter_cmd, /* ESP_CMDP */
- esp_enter_status, /* ESP_STATP */
- esp_enter_badphase, /* ESP_STAT_PMSG */
- esp_enter_badphase, /* ESP_STAT_PMSG | ESP_STAT_PIO */
- esp_enter_msgout, /* ESP_MOP */
- esp_enter_msgin, /* ESP_MIP */
-};
-
-/* The target has control of the bus and we have to see where it has
- * taken us.
- */
-static int esp_do_phase_determine(struct esp *esp)
-{
- if ((esp->ireg & ESP_INTR_DC) != 0)
- return esp_disconnect_amidst_phases(esp);
- return phase_vector[esp->sreg & ESP_STAT_PMASK](esp);
-}
-
-/* First interrupt after exec'ing a cmd comes here. */
-static int esp_select_complete(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
- struct esp_device *esp_dev = SCptr->device->hostdata;
- int cmd_bytes_sent, fcnt;
-
- if (esp->erev != fashme)
- esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS);
-
- if (esp->erev == fashme)
- fcnt = esp->hme_fifo_workaround_count;
- else
- fcnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES);
-
- cmd_bytes_sent = esp_bytes_sent(esp, fcnt);
- dma_invalidate(esp);
-
- /* Let's check to see if a reselect happened
- * while we we're trying to select. This must
- * be checked first.
- */
- if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
- esp_reconnect(esp, SCptr);
- return esp_do_reconnect(esp);
- }
-
- /* Looks like things worked, we should see a bus service &
- * a function complete interrupt at this point. Note we
- * are doing a direct comparison because we don't want to
- * be fooled into thinking selection was successful if
- * ESP_INTR_DC is set, see below.
- */
- if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
- /* target speaks... */
- esp->targets_present |= (1<<SCptr->device->id);
-
- /* What if the target ignores the sdtr? */
- if (esp->snip)
- esp_dev->sync = 1;
-
- /* See how far, if at all, we got in getting
- * the information out to the target.
- */
- switch (esp->seqreg) {
- default:
-
- case ESP_STEP_ASEL:
- /* Arbitration won, target selected, but
- * we are in some phase which is not command
- * phase nor is it message out phase.
- *
- * XXX We've confused the target, obviously.
- * XXX So clear it's state, but we also end
- * XXX up clearing everyone elses. That isn't
- * XXX so nice. I'd like to just reset this
- * XXX target, but if I cannot even get it's
- * XXX attention and finish selection to talk
- * XXX to it, there is not much more I can do.
- * XXX If we have a loaded bus we're going to
- * XXX spend the next second or so renegotiating
- * XXX for synchronous transfers.
- */
- ESPLOG(("esp%d: STEP_ASEL for tgt %d\n",
- esp->esp_id, SCptr->device->id));
-
- case ESP_STEP_SID:
- /* Arbitration won, target selected, went
- * to message out phase, sent one message
- * byte, then we stopped. ATN is asserted
- * on the SCSI bus and the target is still
- * there hanging on. This is a legal
- * sequence step if we gave the ESP a select
- * and stop command.
- *
- * XXX See above, I could set the borken flag
- * XXX in the device struct and retry the
- * XXX command. But would that help for
- * XXX tagged capable targets?
- */
-
- case ESP_STEP_NCMD:
- /* Arbitration won, target selected, maybe
- * sent the one message byte in message out
- * phase, but we did not go to command phase
- * in the end. Actually, we could have sent
- * only some of the message bytes if we tried
- * to send out the entire identify and tag
- * message using ESP_CMD_SA3.
- */
- cmd_bytes_sent = 0;
- break;
-
- case ESP_STEP_PPC:
- /* No, not the powerPC pinhead. Arbitration
- * won, all message bytes sent if we went to
- * message out phase, went to command phase
- * but only part of the command was sent.
- *
- * XXX I've seen this, but usually in conjunction
- * XXX with a gross error which appears to have
- * XXX occurred between the time I told the
- * XXX ESP to arbitrate and when I got the
- * XXX interrupt. Could I have misloaded the
- * XXX command bytes into the fifo? Actually,
- * XXX I most likely missed a phase, and therefore
- * XXX went into never never land and didn't even
- * XXX know it. That was the old driver though.
- * XXX What is even more peculiar is that the ESP
- * XXX showed the proper function complete and
- * XXX bus service bits in the interrupt register.
- */
-
- case ESP_STEP_FINI4:
- case ESP_STEP_FINI5:
- case ESP_STEP_FINI6:
- case ESP_STEP_FINI7:
- /* Account for the identify message */
- if (SCptr->SCp.phase == in_slct_norm)
- cmd_bytes_sent -= 1;
- };
-
- if (esp->erev != fashme)
- esp_cmd(esp, ESP_CMD_NULL);
-
- /* Be careful, we could really get fucked during synchronous
- * data transfers if we try to flush the fifo now.
- */
- if ((esp->erev != fashme) && /* not a Happy Meal and... */
- !fcnt && /* Fifo is empty and... */
- /* either we are not doing synchronous transfers or... */
- (!esp_dev->sync_max_offset ||
- /* We are not going into data in phase. */
- ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
- esp_cmd(esp, ESP_CMD_FLUSH); /* flush is safe */
-
- /* See how far we got if this is not a slow command. */
- if (!esp->esp_slowcmd) {
- if (cmd_bytes_sent < 0)
- cmd_bytes_sent = 0;
- if (cmd_bytes_sent != SCptr->cmd_len) {
- /* Crapola, mark it as a slowcmd
- * so that we have some chance of
- * keeping the command alive with
- * good luck.
- *
- * XXX Actually, if we didn't send it all
- * XXX this means either we didn't set things
- * XXX up properly (driver bug) or the target
- * XXX or the ESP detected parity on one of
- * XXX the command bytes. This makes much
- * XXX more sense, and therefore this code
- * XXX should be changed to send out a
- * XXX parity error message or if the status
- * XXX register shows no parity error then
- * XXX just expect the target to bring the
- * XXX bus into message in phase so that it
- * XXX can send us the parity error message.
- * XXX SCSI sucks...
- */
- esp->esp_slowcmd = 1;
- esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]);
- esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent);
- }
- }
-
- /* Now figure out where we went. */
- esp_advance_phase(SCptr, in_the_dark);
- return esp_do_phase_determine(esp);
- }
-
- /* Did the target even make it? */
- if (esp->ireg == ESP_INTR_DC) {
- /* wheee... nobody there or they didn't like
- * what we told it to do, clean up.
- */
-
- /* If anyone is off the bus, but working on
- * a command in the background for us, tell
- * the ESP to listen for them.
- */
- if (esp->disconnected_SC)
- esp_cmd(esp, ESP_CMD_ESEL);
-
- if (((1<<SCptr->device->id) & esp->targets_present) &&
- esp->seqreg != 0 &&
- (esp->cur_msgout[0] == EXTENDED_MESSAGE) &&
- (SCptr->SCp.phase == in_slct_msg ||
- SCptr->SCp.phase == in_slct_stop)) {
- /* shit */
- esp->snip = 0;
- ESPLOG(("esp%d: Failed synchronous negotiation for target %d "
- "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun));
- esp_dev->sync_max_offset = 0;
- esp_dev->sync_min_period = 0;
- esp_dev->sync = 1; /* so we don't negotiate again */
-
- /* Run the command again, this time though we
- * won't try to negotiate for synchronous transfers.
- *
- * XXX I'd like to do something like send an
- * XXX INITIATOR_ERROR or ABORT message to the
- * XXX target to tell it, "Sorry I confused you,
- * XXX please come back and I will be nicer next
- * XXX time". But that requires having the target
- * XXX on the bus, and it has dropped BSY on us.
- */
- esp->current_SC = NULL;
- esp_advance_phase(SCptr, not_issued);
- prepend_SC(&esp->issue_SC, SCptr);
- esp_exec_cmd(esp);
- return do_intr_end;
- }
-
- /* Ok, this is normal, this is what we see during boot
- * or whenever when we are scanning the bus for targets.
- * But first make sure that is really what is happening.
- */
- if (((1<<SCptr->device->id) & esp->targets_present)) {
- ESPLOG(("esp%d: Warning, live target %d not responding to "
- "selection.\n", esp->esp_id, SCptr->device->id));
-
- /* This _CAN_ happen. The SCSI standard states that
- * the target is to _not_ respond to selection if
- * _it_ detects bad parity on the bus for any reason.
- * Therefore, we assume that if we've talked successfully
- * to this target before, bad parity is the problem.
- */
- esp_done(esp, (DID_PARITY << 16));
- } else {
- /* Else, there really isn't anyone there. */
- ESPMISC(("esp: selection failure, maybe nobody there?\n"));
- ESPMISC(("esp: target %d lun %d\n",
- SCptr->device->id, SCptr->device->lun));
- esp_done(esp, (DID_BAD_TARGET << 16));
- }
- return do_intr_end;
- }
-
- ESPLOG(("esp%d: Selection failure.\n", esp->esp_id));
- printk("esp%d: Currently -- ", esp->esp_id);
- esp_print_ireg(esp->ireg); printk(" ");
- esp_print_statreg(esp->sreg); printk(" ");
- esp_print_seqreg(esp->seqreg); printk("\n");
- printk("esp%d: New -- ", esp->esp_id);
- esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
- esp->seqreg = sbus_readb(esp->eregs + ESP_SSTEP);
- esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
- esp_print_ireg(esp->ireg); printk(" ");
- esp_print_statreg(esp->sreg); printk(" ");
- esp_print_seqreg(esp->seqreg); printk("\n");
- ESPLOG(("esp%d: resetting bus\n", esp->esp_id));
- return do_reset_bus; /* ugh... */
-}
-
-/* Continue reading bytes for msgin phase. */
-static int esp_do_msgincont(struct esp *esp)
-{
- if (esp->ireg & ESP_INTR_BSERV) {
- /* in the right phase too? */
- if ((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) {
- /* phew... */
- esp_cmd(esp, ESP_CMD_TI);
- esp_advance_phase(esp->current_SC, in_msgindone);
- return do_intr_end;
- }
-
- /* We changed phase but ESP shows bus service,
- * in this case it is most likely that we, the
- * hacker who has been up for 20hrs straight
- * staring at the screen, drowned in coffee
- * smelling like retched cigarette ashes
- * have miscoded something..... so, try to
- * recover as best we can.
- */
- ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id));
- }
- esp_advance_phase(esp->current_SC, in_the_dark);
- return do_phase_determine;
-}
-
-static int check_singlebyte_msg(struct esp *esp)
-{
- esp->prevmsgin = esp->cur_msgin[0];
- if (esp->cur_msgin[0] & 0x80) {
- /* wheee... */
- ESPLOG(("esp%d: target sends identify amidst phases\n",
- esp->esp_id));
- esp_advance_phase(esp->current_SC, in_the_dark);
- return 0;
- } else if (((esp->cur_msgin[0] & 0xf0) == 0x20) ||
- (esp->cur_msgin[0] == EXTENDED_MESSAGE)) {
- esp->msgin_len = 2;
- esp_advance_phase(esp->current_SC, in_msgincont);
- return 0;
- }
- esp_advance_phase(esp->current_SC, in_the_dark);
- switch (esp->cur_msgin[0]) {
- default:
- /* We don't want to hear about it. */
- ESPLOG(("esp%d: msg %02x which we don't know about\n", esp->esp_id,
- esp->cur_msgin[0]));
- return MESSAGE_REJECT;
-
- case NOP:
- ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id,
- esp->current_SC->device->id));
- return 0;
-
- case RESTORE_POINTERS:
- /* In this case we might also have to backup the
- * "slow command" pointer. It is rare to get such
- * a save/restore pointer sequence so early in the
- * bus transition sequences, but cover it.
- */
- if (esp->esp_slowcmd) {
- esp->esp_scmdleft = esp->current_SC->cmd_len;
- esp->esp_scmdp = &esp->current_SC->cmnd[0];
- }
- esp_restore_pointers(esp, esp->current_SC);
- return 0;
-
- case SAVE_POINTERS:
- esp_save_pointers(esp, esp->current_SC);
- return 0;
-
- case COMMAND_COMPLETE:
- case DISCONNECT:
- /* Freeing the bus, let it go. */
- esp->current_SC->SCp.phase = in_freeing;
- return 0;
-
- case MESSAGE_REJECT:
- ESPMISC(("msg reject, "));
- if (esp->prevmsgout == EXTENDED_MESSAGE) {
- struct esp_device *esp_dev = esp->current_SC->device->hostdata;
-
- /* Doesn't look like this target can
- * do synchronous or WIDE transfers.
- */
- ESPSDTR(("got reject, was trying nego, clearing sync/WIDE\n"));
- esp_dev->sync = 1;
- esp_dev->wide = 1;
- esp_dev->sync_min_period = 0;
- esp_dev->sync_max_offset = 0;
- return 0;
- } else {
- ESPMISC(("not sync nego, sending ABORT\n"));
- return ABORT;
- }
- };
-}
-
-/* Target negotiates for synchronous transfers before we do, this
- * is legal although very strange. What is even funnier is that
- * the SCSI2 standard specifically recommends against targets doing
- * this because so many initiators cannot cope with this occurring.
- */
-static int target_with_ants_in_pants(struct esp *esp,
- struct scsi_cmnd *SCptr,
- struct esp_device *esp_dev)
-{
- if (esp_dev->sync || SCptr->device->borken) {
- /* sorry, no can do */
- ESPSDTR(("forcing to async, "));
- build_sync_nego_msg(esp, 0, 0);
- esp_dev->sync = 1;
- esp->snip = 1;
- ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id));
- esp_advance_phase(SCptr, in_the_dark);
- return EXTENDED_MESSAGE;
- }
-
- /* Ok, we'll check them out... */
- return 0;
-}
-
-static void sync_report(struct esp *esp)
-{
- int msg3, msg4;
- char *type;
-
- msg3 = esp->cur_msgin[3];
- msg4 = esp->cur_msgin[4];
- if (msg4) {
- int hz = 1000000000 / (msg3 * 4);
- int integer = hz / 1000000;
- int fraction = (hz - (integer * 1000000)) / 10000;
- if ((esp->erev == fashme) &&
- (esp->config3[esp->current_SC->device->id] & ESP_CONFIG3_EWIDE)) {
- type = "FAST-WIDE";
- integer <<= 1;
- fraction <<= 1;
- } else if ((msg3 * 4) < 200) {
- type = "FAST";
- } else {
- type = "synchronous";
- }
-
- /* Do not transform this back into one big printk
- * again, it triggers a bug in our sparc64-gcc272
- * sibling call optimization. -DaveM
- */
- ESPLOG((KERN_INFO "esp%d: target %d ",
- esp->esp_id, esp->current_SC->device->id));
- ESPLOG(("[period %dns offset %d %d.%02dMHz ",
- (int) msg3 * 4, (int) msg4,
- integer, fraction));
- ESPLOG(("%s SCSI%s]\n", type,
- (((msg3 * 4) < 200) ? "-II" : "")));
- } else {
- ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n",
- esp->esp_id, esp->current_SC->device->id));
- }
-}
-
-static int check_multibyte_msg(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
- struct esp_device *esp_dev = SCptr->device->hostdata;
- u8 regval = 0;
- int message_out = 0;
-
- ESPSDTR(("chk multibyte msg: "));
- if (esp->cur_msgin[2] == EXTENDED_SDTR) {
- int period = esp->cur_msgin[3];
- int offset = esp->cur_msgin[4];
-
- ESPSDTR(("is sync nego response, "));
- if (!esp->snip) {
- int rval;
-
- /* Target negotiates first! */
- ESPSDTR(("target jumps the gun, "));
- message_out = EXTENDED_MESSAGE; /* we must respond */
- rval = target_with_ants_in_pants(esp, SCptr, esp_dev);
- if (rval)
- return rval;
- }
-
- ESPSDTR(("examining sdtr, "));
-
- /* Offset cannot be larger than ESP fifo size. */
- if (offset > 15) {
- ESPSDTR(("offset too big %2x, ", offset));
- offset = 15;
- ESPSDTR(("sending back new offset\n"));
- build_sync_nego_msg(esp, period, offset);
- return EXTENDED_MESSAGE;
- }
-
- if (offset && period > esp->max_period) {
- /* Yeee, async for this slow device. */
- ESPSDTR(("period too long %2x, ", period));
- build_sync_nego_msg(esp, 0, 0);
- ESPSDTR(("hoping for msgout\n"));
- esp_advance_phase(esp->current_SC, in_the_dark);
- return EXTENDED_MESSAGE;
- } else if (offset && period < esp->min_period) {
- ESPSDTR(("period too short %2x, ", period));
- period = esp->min_period;
- if (esp->erev > esp236)
- regval = 4;
- else
- regval = 5;
- } else if (offset) {
- int tmp;
-
- ESPSDTR(("period is ok, "));
- tmp = esp->ccycle / 1000;
- regval = (((period << 2) + tmp - 1) / tmp);
- if (regval && ((esp->erev == fas100a ||
- esp->erev == fas236 ||
- esp->erev == fashme))) {
- if (period >= 50)
- regval--;
- }
- }
-
- if (offset) {
- u8 bit;
-
- esp_dev->sync_min_period = (regval & 0x1f);
- esp_dev->sync_max_offset = (offset | esp->radelay);
- if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) {
- if ((esp->erev == fas100a) || (esp->erev == fashme))
- bit = ESP_CONFIG3_FAST;
- else
- bit = ESP_CONFIG3_FSCSI;
- if (period < 50) {
- /* On FAS366, if using fast-20 synchronous transfers
- * we need to make sure the REQ/ACK assert/deassert
- * control bits are clear.
- */
- if (esp->erev == fashme)
- esp_dev->sync_max_offset &= ~esp->radelay;
- esp->config3[SCptr->device->id] |= bit;
- } else {
- esp->config3[SCptr->device->id] &= ~bit;
- }
- esp->prev_cfg3 = esp->config3[SCptr->device->id];
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
- }
- esp->prev_soff = esp_dev->sync_max_offset;
- esp->prev_stp = esp_dev->sync_min_period;
- sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
- sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
- ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
- esp_dev->sync_max_offset,
- esp_dev->sync_min_period,
- esp->config3[SCptr->device->id]));
-
- esp->snip = 0;
- } else if (esp_dev->sync_max_offset) {
- u8 bit;
-
- /* back to async mode */
- ESPSDTR(("unaccaptable sync nego, forcing async\n"));
- esp_dev->sync_max_offset = 0;
- esp_dev->sync_min_period = 0;
- esp->prev_soff = 0;
- esp->prev_stp = 0;
- sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
- sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
- if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) {
- if ((esp->erev == fas100a) || (esp->erev == fashme))
- bit = ESP_CONFIG3_FAST;
- else
- bit = ESP_CONFIG3_FSCSI;
- esp->config3[SCptr->device->id] &= ~bit;
- esp->prev_cfg3 = esp->config3[SCptr->device->id];
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
- }
- }
-
- sync_report(esp);
-
- ESPSDTR(("chk multibyte msg: sync is known, "));
- esp_dev->sync = 1;
-
- if (message_out) {
- ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n",
- esp->esp_id));
- build_sync_nego_msg(esp, period, offset);
- esp_advance_phase(SCptr, in_the_dark);
- return EXTENDED_MESSAGE;
- }
-
- ESPSDTR(("returning zero\n"));
- esp_advance_phase(SCptr, in_the_dark); /* ...or else! */
- return 0;
- } else if (esp->cur_msgin[2] == EXTENDED_WDTR) {
- int size = 8 << esp->cur_msgin[3];
-
- esp->wnip = 0;
- if (esp->erev != fashme) {
- ESPLOG(("esp%d: AIEEE wide msg received and not HME.\n",
- esp->esp_id));
- message_out = MESSAGE_REJECT;
- } else if (size > 16) {
- ESPLOG(("esp%d: AIEEE wide transfer for %d size "
- "not supported.\n", esp->esp_id, size));
- message_out = MESSAGE_REJECT;
- } else {
- /* Things look good; let's see what we got. */
- if (size == 16) {
- /* Set config 3 register for this target. */
- esp->config3[SCptr->device->id] |= ESP_CONFIG3_EWIDE;
- } else {
- /* Just make sure it was one byte sized. */
- if (size != 8) {
- ESPLOG(("esp%d: Aieee, wide nego of %d size.\n",
- esp->esp_id, size));
- message_out = MESSAGE_REJECT;
- goto finish;
- }
- /* Pure paranoia. */
- esp->config3[SCptr->device->id] &= ~(ESP_CONFIG3_EWIDE);
- }
- esp->prev_cfg3 = esp->config3[SCptr->device->id];
- sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
-
- /* Regardless, next try for sync transfers. */
- build_sync_nego_msg(esp, esp->sync_defp, 15);
- esp_dev->sync = 1;
- esp->snip = 1;
- message_out = EXTENDED_MESSAGE;
- }
- } else if (esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) {
- ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id));
- message_out = MESSAGE_REJECT;
- }
-finish:
- esp_advance_phase(SCptr, in_the_dark);
- return message_out;
-}
-
-static int esp_do_msgindone(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
- int message_out = 0, it = 0, rval;
-
- rval = skipahead1(esp, SCptr, in_msgin, in_msgindone);
- if (rval)
- return rval;
- if (SCptr->SCp.sent_command != in_status) {
- if (!(esp->ireg & ESP_INTR_DC)) {
- if (esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) {
- message_out = MSG_PARITY_ERROR;
- esp_cmd(esp, ESP_CMD_FLUSH);
- } else if (esp->erev != fashme &&
- (it = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES)) != 1) {
- /* We certainly dropped the ball somewhere. */
- message_out = INITIATOR_ERROR;
- esp_cmd(esp, ESP_CMD_FLUSH);
- } else if (!esp->msgin_len) {
- if (esp->erev == fashme)
- it = esp->hme_fifo_workaround_buffer[0];
- else
- it = sbus_readb(esp->eregs + ESP_FDATA);
- esp_advance_phase(SCptr, in_msgincont);
- } else {
- /* it is ok and we want it */
- if (esp->erev == fashme)
- it = esp->cur_msgin[esp->msgin_ctr] =
- esp->hme_fifo_workaround_buffer[0];
- else
- it = esp->cur_msgin[esp->msgin_ctr] =
- sbus_readb(esp->eregs + ESP_FDATA);
- esp->msgin_ctr++;
- }
- } else {
- esp_advance_phase(SCptr, in_the_dark);
- return do_work_bus;
- }
- } else {
- it = esp->cur_msgin[0];
- }
- if (!message_out && esp->msgin_len) {
- if (esp->msgin_ctr < esp->msgin_len) {
- esp_advance_phase(SCptr, in_msgincont);
- } else if (esp->msgin_len == 1) {
- message_out = check_singlebyte_msg(esp);
- } else if (esp->msgin_len == 2) {
- if (esp->cur_msgin[0] == EXTENDED_MESSAGE) {
- if ((it + 2) >= 15) {
- message_out = MESSAGE_REJECT;
- } else {
- esp->msgin_len = (it + 2);
- esp_advance_phase(SCptr, in_msgincont);
- }
- } else {
- message_out = MESSAGE_REJECT; /* foo on you */
- }
- } else {
- message_out = check_multibyte_msg(esp);
- }
- }
- if (message_out < 0) {
- return -message_out;
- } else if (message_out) {
- if (((message_out != 1) &&
- ((message_out < 0x20) || (message_out & 0x80))))
- esp->msgout_len = 1;
- esp->cur_msgout[0] = message_out;
- esp_cmd(esp, ESP_CMD_SATN);
- esp_advance_phase(SCptr, in_the_dark);
- esp->msgin_len = 0;
- }
- esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
- esp->sreg &= ~(ESP_STAT_INTR);
- if ((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD))
- esp_cmd(esp, ESP_CMD_MOK);
- if ((SCptr->SCp.sent_command == in_msgindone) &&
- (SCptr->SCp.phase == in_freeing))
- return esp_do_freebus(esp);
- return do_intr_end;
-}
-
-static int esp_do_cmdbegin(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
-
- esp_advance_phase(SCptr, in_cmdend);
- if (esp->erev == fashme) {
- u32 tmp = sbus_readl(esp->dregs + DMA_CSR);
- int i;
-
- for (i = 0; i < esp->esp_scmdleft; i++)
- esp->esp_command[i] = *esp->esp_scmdp++;
- esp->esp_scmdleft = 0;
- esp_cmd(esp, ESP_CMD_FLUSH);
- esp_setcount(esp->eregs, i, 1);
- esp_cmd(esp, (ESP_CMD_DMA | ESP_CMD_TI));
- tmp |= (DMA_SCSI_DISAB | DMA_ENABLE);
- tmp &= ~(DMA_ST_WRITE);
- sbus_writel(i, esp->dregs + DMA_COUNT);
- sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
- sbus_writel(tmp, esp->dregs + DMA_CSR);
- } else {
- u8 tmp;
-
- esp_cmd(esp, ESP_CMD_FLUSH);
- tmp = *esp->esp_scmdp++;
- esp->esp_scmdleft--;
- sbus_writeb(tmp, esp->eregs + ESP_FDATA);
- esp_cmd(esp, ESP_CMD_TI);
- }
- return do_intr_end;
-}
-
-static int esp_do_cmddone(struct esp *esp)
-{
- if (esp->erev == fashme)
- dma_invalidate(esp);
- else
- esp_cmd(esp, ESP_CMD_NULL);
-
- if (esp->ireg & ESP_INTR_BSERV) {
- esp_advance_phase(esp->current_SC, in_the_dark);
- return esp_do_phase_determine(esp);
- }
-
- ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n",
- esp->esp_id));
- return do_reset_bus;
-}
-
-static int esp_do_msgout(struct esp *esp)
-{
- esp_cmd(esp, ESP_CMD_FLUSH);
- switch (esp->msgout_len) {
- case 1:
- if (esp->erev == fashme)
- hme_fifo_push(esp, &esp->cur_msgout[0], 1);
- else
- sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA);
-
- esp_cmd(esp, ESP_CMD_TI);
- break;
-
- case 2:
- esp->esp_command[0] = esp->cur_msgout[0];
- esp->esp_command[1] = esp->cur_msgout[1];
-
- if (esp->erev == fashme) {
- hme_fifo_push(esp, &esp->cur_msgout[0], 2);
- esp_cmd(esp, ESP_CMD_TI);
- } else {
- dma_setup(esp, esp->esp_command_dvma, 2, 0);
- esp_setcount(esp->eregs, 2, 0);
- esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
- }
- break;
-
- case 4:
- esp->esp_command[0] = esp->cur_msgout[0];
- esp->esp_command[1] = esp->cur_msgout[1];
- esp->esp_command[2] = esp->cur_msgout[2];
- esp->esp_command[3] = esp->cur_msgout[3];
- esp->snip = 1;
-
- if (esp->erev == fashme) {
- hme_fifo_push(esp, &esp->cur_msgout[0], 4);
- esp_cmd(esp, ESP_CMD_TI);
- } else {
- dma_setup(esp, esp->esp_command_dvma, 4, 0);
- esp_setcount(esp->eregs, 4, 0);
- esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
- }
- break;
-
- case 5:
- esp->esp_command[0] = esp->cur_msgout[0];
- esp->esp_command[1] = esp->cur_msgout[1];
- esp->esp_command[2] = esp->cur_msgout[2];
- esp->esp_command[3] = esp->cur_msgout[3];
- esp->esp_command[4] = esp->cur_msgout[4];
- esp->snip = 1;
-
- if (esp->erev == fashme) {
- hme_fifo_push(esp, &esp->cur_msgout[0], 5);
- esp_cmd(esp, ESP_CMD_TI);
- } else {
- dma_setup(esp, esp->esp_command_dvma, 5, 0);
- esp_setcount(esp->eregs, 5, 0);
- esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
- }
- break;
-
- default:
- /* whoops */
- ESPMISC(("bogus msgout sending NOP\n"));
- esp->cur_msgout[0] = NOP;
-
- if (esp->erev == fashme) {
- hme_fifo_push(esp, &esp->cur_msgout[0], 1);
- } else {
- sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA);
- }
-
- esp->msgout_len = 1;
- esp_cmd(esp, ESP_CMD_TI);
- break;
- };
-
- esp_advance_phase(esp->current_SC, in_msgoutdone);
- return do_intr_end;
-}
-
-static int esp_do_msgoutdone(struct esp *esp)
-{
- if (esp->msgout_len > 1) {
- /* XXX HME/FAS ATN deassert workaround required,
- * XXX no DMA flushing, only possible ESP_CMD_FLUSH
- * XXX to kill the fifo.
- */
- if (esp->erev != fashme) {
- u32 tmp;
-
- while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ)
- udelay(1);
- tmp &= ~DMA_ENABLE;
- sbus_writel(tmp, esp->dregs + DMA_CSR);
- dma_invalidate(esp);
- } else {
- esp_cmd(esp, ESP_CMD_FLUSH);
- }
- }
- if (!(esp->ireg & ESP_INTR_DC)) {
- if (esp->erev != fashme)
- esp_cmd(esp, ESP_CMD_NULL);
- switch (esp->sreg & ESP_STAT_PMASK) {
- case ESP_MOP:
- /* whoops, parity error */
- ESPLOG(("esp%d: still in msgout, parity error assumed\n",
- esp->esp_id));
- if (esp->msgout_len > 1)
- esp_cmd(esp, ESP_CMD_SATN);
- esp_advance_phase(esp->current_SC, in_msgout);
- return do_work_bus;
-
- case ESP_DIP:
- break;
-
- default:
- /* Happy Meal fifo is touchy... */
- if ((esp->erev != fashme) &&
- !fcount(esp) &&
- !(((struct esp_device *)esp->current_SC->device->hostdata)->sync_max_offset))
- esp_cmd(esp, ESP_CMD_FLUSH);
- break;
-
- };
- } else {
- ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id));
- return do_reset_bus;
- }
-
- /* If we sent out a synchronous negotiation message, update
- * our state.
- */
- if (esp->cur_msgout[2] == EXTENDED_MESSAGE &&
- esp->cur_msgout[4] == EXTENDED_SDTR) {
- esp->snip = 1; /* anal retentiveness... */
- }
-
- esp->prevmsgout = esp->cur_msgout[0];
- esp->msgout_len = 0;
- esp_advance_phase(esp->current_SC, in_the_dark);
- return esp_do_phase_determine(esp);
-}
-
-static int esp_bus_unexpected(struct esp *esp)
-{
- ESPLOG(("esp%d: command in weird state %2x\n",
- esp->esp_id, esp->current_SC->SCp.phase));
- return do_reset_bus;
-}
-
-static espfunc_t bus_vector[] = {
- esp_do_data_finale,
- esp_do_data_finale,
- esp_bus_unexpected,
- esp_do_msgin,
- esp_do_msgincont,
- esp_do_msgindone,
- esp_do_msgout,
- esp_do_msgoutdone,
- esp_do_cmdbegin,
- esp_do_cmddone,
- esp_do_status,
- esp_do_freebus,
- esp_do_phase_determine,
- esp_bus_unexpected,
- esp_bus_unexpected,
- esp_bus_unexpected,
-};
-
-/* This is the second tier in our dual-level SCSI state machine. */
-static int esp_work_bus(struct esp *esp)
-{
- struct scsi_cmnd *SCptr = esp->current_SC;
- unsigned int phase;
-
- ESPBUS(("esp_work_bus: "));
- if (!SCptr) {
- ESPBUS(("reconnect\n"));
- return esp_do_reconnect(esp);
- }
- phase = SCptr->SCp.phase;
- if ((phase & 0xf0) == in_phases_mask)
- return bus_vector[(phase & 0x0f)](esp);
- else if ((phase & 0xf0) == in_slct_mask)
- return esp_select_complete(esp);
- else
- return esp_bus_unexpected(esp);
-}
-
-static espfunc_t isvc_vector[] = {
- NULL,
- esp_do_phase_determine,
- esp_do_resetbus,
- esp_finish_reset,
- esp_work_bus
-};
-
-/* Main interrupt handler for an esp adapter. */
-static void esp_handle(struct esp *esp)
-{
- struct scsi_cmnd *SCptr;
- int what_next = do_intr_end;
-
- SCptr = esp->current_SC;
-
- /* Check for errors. */
- esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
- esp->sreg &= (~ESP_STAT_INTR);
- if (esp->erev == fashme) {
- esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
- esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS);
- }
-
- if (esp->sreg & (ESP_STAT_SPAM)) {
- /* Gross error, could be due to one of:
- *
- * - top of fifo overwritten, could be because
- * we tried to do a synchronous transfer with
- * an offset greater than ESP fifo size
- *
- * - top of command register overwritten
- *
- * - DMA setup to go in one direction, SCSI
- * bus points in the other, whoops
- *
- * - weird phase change during asynchronous
- * data phase while we are initiator
- */
- ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg));
-
- /* If a command is live on the bus we cannot safely
- * reset the bus, so we'll just let the pieces fall
- * where they may. Here we are hoping that the
- * target will be able to cleanly go away soon
- * so we can safely reset things.
- */
- if (!SCptr) {
- ESPLOG(("esp%d: No current cmd during gross error, "
- "resetting bus\n", esp->esp_id));
- what_next = do_reset_bus;
- goto state_machine;
- }
- }
-
- if (sbus_readl(esp->dregs + DMA_CSR) & DMA_HNDL_ERROR) {
- /* A DMA gate array error. Here we must
- * be seeing one of two things. Either the
- * virtual to physical address translation
- * on the SBUS could not occur, else the
- * translation it did get pointed to a bogus
- * page. Ho hum...
- */
- ESPLOG(("esp%d: DMA error %08x\n", esp->esp_id,
- sbus_readl(esp->dregs + DMA_CSR)));
-
- /* DMA gate array itself must be reset to clear the
- * error condition.
- */
- esp_reset_dma(esp);
-
- what_next = do_reset_bus;
- goto state_machine;
- }
-
- esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); /* Unlatch intr reg */
-
- if (esp->erev == fashme) {
- /* This chip is really losing. */
- ESPHME(("HME["));
-
- ESPHME(("sreg2=%02x,", esp->sreg2));
- /* Must latch fifo before reading the interrupt
- * register else garbage ends up in the FIFO
- * which confuses the driver utterly.
- */
- if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
- (esp->sreg2 & ESP_STAT2_F1BYTE)) {
- ESPHME(("fifo_workaround]"));
- hme_fifo_read(esp);
- } else {
- ESPHME(("no_fifo_workaround]"));
- }
- }
-
- /* No current cmd is only valid at this point when there are
- * commands off the bus or we are trying a reset.
- */
- if (!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) {
- /* Panic is safe, since current_SC is null. */
- ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id));
- panic("esp_handle: current_SC == penguin within interrupt!");
- }
-
- if (esp->ireg & (ESP_INTR_IC)) {
- /* Illegal command fed to ESP. Outside of obvious
- * software bugs that could cause this, there is
- * a condition with esp100 where we can confuse the
- * ESP into an erroneous illegal command interrupt
- * because it does not scrape the FIFO properly
- * for reselection. See esp100_reconnect_hwbug()
- * to see how we try very hard to avoid this.
- */
- ESPLOG(("esp%d: invalid command\n", esp->esp_id));
-
- esp_dump_state(esp);
-
- if (SCptr != NULL) {
- /* Devices with very buggy firmware can drop BSY
- * during a scatter list interrupt when using sync
- * mode transfers. We continue the transfer as
- * expected, the target drops the bus, the ESP
- * gets confused, and we get a illegal command
- * interrupt because the bus is in the disconnected
- * state now and ESP_CMD_TI is only allowed when
- * a nexus is alive on the bus.
- */
- ESPLOG(("esp%d: Forcing async and disabling disconnect for "
- "target %d\n", esp->esp_id, SCptr->device->id));
- SCptr->device->borken = 1; /* foo on you */
- }
-
- what_next = do_reset_bus;
- } else if (!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
- if (SCptr) {
- unsigned int phase = SCptr->SCp.phase;
-
- if (phase & in_phases_mask) {
- what_next = esp_work_bus(esp);
- } else if (phase & in_slct_mask) {
- what_next = esp_select_complete(esp);
- } else {
- ESPLOG(("esp%d: interrupt for no good reason...\n",
- esp->esp_id));
- what_next = do_intr_end;
- }
- } else {
- ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n",
- esp->esp_id));
- what_next = do_reset_bus;
- }
- } else if (esp->ireg & ESP_INTR_SR) {
- ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id));
- what_next = do_reset_complete;
- } else if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) {
- ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n",
- esp->esp_id));
- what_next = do_reset_bus;
- } else if (esp->ireg & ESP_INTR_RSEL) {
- if (SCptr == NULL) {
- /* This is ok. */
- what_next = esp_do_reconnect(esp);
- } else if (SCptr->SCp.phase & in_slct_mask) {
- /* Only selection code knows how to clean
- * up properly.
- */
- ESPDISC(("Reselected during selection attempt\n"));
- what_next = esp_select_complete(esp);
- } else {
- ESPLOG(("esp%d: Reselected while bus is busy\n",
- esp->esp_id));
- what_next = do_reset_bus;
- }
- }
-
- /* This is tier-one in our dual level SCSI state machine. */
-state_machine:
- while (what_next != do_intr_end) {
- if (what_next >= do_phase_determine &&
- what_next < do_intr_end) {
- what_next = isvc_vector[what_next](esp);
- } else {
- /* state is completely lost ;-( */
- ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
- esp->esp_id));
- what_next = do_reset_bus;
- }
- }
-}
-
-/* Service only the ESP described by dev_id. */
-static irqreturn_t esp_intr(int irq, void *dev_id)
-{
- struct esp *esp = dev_id;
- unsigned long flags;
-
- spin_lock_irqsave(esp->ehost->host_lock, flags);
- if (ESP_IRQ_P(esp->dregs)) {
- ESP_INTSOFF(esp->dregs);
-
- ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id));
- esp_handle(esp);
- ESPIRQ((")"));
-
- ESP_INTSON(esp->dregs);
- }
- spin_unlock_irqrestore(esp->ehost->host_lock, flags);
-
- return IRQ_HANDLED;
-}
-
-static int esp_slave_alloc(struct scsi_device *SDptr)
-{
- struct esp_device *esp_dev =
- kmalloc(sizeof(struct esp_device), GFP_ATOMIC);
-
- if (!esp_dev)
- return -ENOMEM;
- memset(esp_dev, 0, sizeof(struct esp_device));
- SDptr->hostdata = esp_dev;
- return 0;
-}
-
-static void esp_slave_destroy(struct scsi_device *SDptr)
-{
- struct esp *esp = (struct esp *) SDptr->host->hostdata;
-
- esp->targets_present &= ~(1 << SDptr->id);
- kfree(SDptr->hostdata);
- SDptr->hostdata = NULL;
-}
-
-static struct scsi_host_template esp_template = {
- .module = THIS_MODULE,
- .name = "esp",
- .info = esp_info,
- .slave_alloc = esp_slave_alloc,
- .slave_destroy = esp_slave_destroy,
- .queuecommand = esp_queue,
- .eh_abort_handler = esp_abort,
- .eh_bus_reset_handler = esp_reset,
- .can_queue = 7,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
- .proc_name = "esp",
- .proc_info = esp_proc_info,
-};
-
-#ifndef CONFIG_SUN4
-static struct of_device_id esp_match[] = {
- {
- .name = "SUNW,esp",
- .data = &esp_template,
- },
- {
- .name = "SUNW,fas",
- .data = &esp_template,
- },
- {
- .name = "esp",
- .data = &esp_template,
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, esp_match);
-
-static struct of_platform_driver esp_sbus_driver = {
- .name = "esp",
- .match_table = esp_match,
- .probe = esp_sbus_probe,
- .remove = __devexit_p(esp_sbus_remove),
-};
-#endif
-
-static int __init esp_init(void)
-{
-#ifdef CONFIG_SUN4
- return esp_sun4_probe(&esp_template);
-#else
- return of_register_driver(&esp_sbus_driver, &sbus_bus_type);
-#endif
-}
-
-static void __exit esp_exit(void)
-{
-#ifdef CONFIG_SUN4
- esp_sun4_remove();
-#else
- of_unregister_driver(&esp_sbus_driver);
-#endif
-}
-
-MODULE_DESCRIPTION("ESP Sun SCSI driver");
-MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRV_VERSION);
-
-module_init(esp_init);
-module_exit(esp_exit);
diff --git a/drivers/scsi/esp.h b/drivers/scsi/esp.h
deleted file mode 100644
index a98cda9121fc..000000000000
--- a/drivers/scsi/esp.h
+++ /dev/null
@@ -1,406 +0,0 @@
-/* $Id: esp.h,v 1.29 2001/12/11 04:55:47 davem Exp $
- * esp.h: Defines and structures for the Sparc ESP (Enhanced SCSI
- * Processor) driver under Linux.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#ifndef _SPARC_ESP_H
-#define _SPARC_ESP_H
-
-/* For dvma controller register definitions. */
-#include <asm/dma.h>
-
-/* The ESP SCSI controllers have their register sets in three
- * "classes":
- *
- * 1) Registers which are both read and write.
- * 2) Registers which are read only.
- * 3) Registers which are write only.
- *
- * Yet, they all live within the same IO space.
- */
-
-/* All the ESP registers are one byte each and are accessed longwords
- * apart with a big-endian ordering to the bytes.
- */
- /* Access Description Offset */
-#define ESP_TCLOW 0x00UL /* rw Low bits of the transfer count 0x00 */
-#define ESP_TCMED 0x04UL /* rw Mid bits of the transfer count 0x04 */
-#define ESP_FDATA 0x08UL /* rw FIFO data bits 0x08 */
-#define ESP_CMD 0x0cUL /* rw SCSI command bits 0x0c */
-#define ESP_STATUS 0x10UL /* ro ESP status register 0x10 */
-#define ESP_BUSID ESP_STATUS /* wo Bus ID for select/reselect 0x10 */
-#define ESP_INTRPT 0x14UL /* ro Kind of interrupt 0x14 */
-#define ESP_TIMEO ESP_INTRPT /* wo Timeout value for select/resel 0x14 */
-#define ESP_SSTEP 0x18UL /* ro Sequence step register 0x18 */
-#define ESP_STP ESP_SSTEP /* wo Transfer period per sync 0x18 */
-#define ESP_FFLAGS 0x1cUL /* ro Bits of current FIFO info 0x1c */
-#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */
-#define ESP_CFG1 0x20UL /* rw First configuration register 0x20 */
-#define ESP_CFACT 0x24UL /* wo Clock conversion factor 0x24 */
-#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */
-#define ESP_CTEST 0x28UL /* wo Chip test register 0x28 */
-#define ESP_CFG2 0x2cUL /* rw Second configuration register 0x2c */
-#define ESP_CFG3 0x30UL /* rw Third configuration register 0x30 */
-#define ESP_TCHI 0x38UL /* rw High bits of transfer count 0x38 */
-#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */
-#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */
-#define ESP_FGRND 0x3cUL /* rw Data base for fifo 0x3c */
-#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */
-#define ESP_REG_SIZE 0x40UL
-
-/* Various revisions of the ESP board. */
-enum esp_rev {
- esp100 = 0x00, /* NCR53C90 - very broken */
- esp100a = 0x01, /* NCR53C90A */
- esp236 = 0x02,
- fas236 = 0x03,
- fas100a = 0x04,
- fast = 0x05,
- fashme = 0x06,
- espunknown = 0x07
-};
-
-/* We allocate one of these for each scsi device and attach it to
- * SDptr->hostdata for use in the driver
- */
-struct esp_device {
- unsigned char sync_min_period;
- unsigned char sync_max_offset;
- unsigned sync:1;
- unsigned wide:1;
- unsigned disconnect:1;
-};
-
-struct scsi_cmnd;
-
-/* We get one of these for each ESP probed. */
-struct esp {
- void __iomem *eregs; /* ESP controller registers */
- void __iomem *dregs; /* DMA controller registers */
- struct sbus_dma *dma; /* DMA controller sw state */
- struct Scsi_Host *ehost; /* Backpointer to SCSI Host */
- struct sbus_dev *sdev; /* Pointer to SBus entry */
-
- /* ESP Configuration Registers */
- u8 config1; /* Copy of the 1st config register */
- u8 config2; /* Copy of the 2nd config register */
- u8 config3[16]; /* Copy of the 3rd config register */
-
- /* The current command we are sending to the ESP chip. This esp_command
- * ptr needs to be mapped in DVMA area so we can send commands and read
- * from the ESP fifo without burning precious CPU cycles. Programmed I/O
- * sucks when we have the DVMA to do it for us. The ESP is stupid and will
- * only send out 6, 10, and 12 byte SCSI commands, others we need to send
- * one byte at a time. esp_slowcmd being set says that we are doing one
- * of the command types ESP doesn't understand, esp_scmdp keeps track of
- * which byte we are sending, esp_scmdleft says how many bytes to go.
- */
- volatile u8 *esp_command; /* Location of command (CPU view) */
- __u32 esp_command_dvma;/* Location of command (DVMA view) */
- unsigned char esp_clen; /* Length of this command */
- unsigned char esp_slowcmd;
- unsigned char *esp_scmdp;
- unsigned char esp_scmdleft;
-
- /* The following are used to determine the cause of an IRQ. Upon every
- * IRQ entry we synchronize these with the hardware registers.
- */
- u8 ireg; /* Copy of ESP interrupt register */
- u8 sreg; /* Copy of ESP status register */
- u8 seqreg; /* Copy of ESP sequence step register */
- u8 sreg2; /* Copy of HME status2 register */
-
- /* To save register writes to the ESP, which can be expensive, we
- * keep track of the previous value that various registers had for
- * the last target we connected to. If they are the same for the
- * current target, we skip the register writes as they are not needed.
- */
- u8 prev_soff, prev_stp;
- u8 prev_cfg3, __cache_pad;
-
- /* We also keep a cache of the previous FAS/HME DMA CSR register value. */
- u32 prev_hme_dmacsr;
-
- /* The HME is the biggest piece of shit I have ever seen. */
- u8 hme_fifo_workaround_buffer[16 * 2];
- u8 hme_fifo_workaround_count;
-
- /* For each target we keep track of save/restore data
- * pointer information. This needs to be updated majorly
- * when we add support for tagged queueing. -DaveM
- */
- struct esp_pointers {
- char *saved_ptr;
- struct scatterlist *saved_buffer;
- int saved_this_residual;
- int saved_buffers_residual;
- } data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/;
-
- /* Clock periods, frequencies, synchronization, etc. */
- unsigned int cfreq; /* Clock frequency in HZ */
- unsigned int cfact; /* Clock conversion factor */
- unsigned int raw_cfact; /* Raw copy from probing */
- unsigned int ccycle; /* One ESP clock cycle */
- unsigned int ctick; /* One ESP clock time */
- unsigned int radelay; /* FAST chip req/ack delay */
- unsigned int neg_defp; /* Default negotiation period */
- unsigned int sync_defp; /* Default sync transfer period */
- unsigned int max_period; /* longest our period can be */
- unsigned int min_period; /* shortest period we can withstand */
-
- struct esp *next; /* Next ESP we probed or NULL */
- char prom_name[64]; /* Name of ESP device from prom */
- int prom_node; /* Prom node where ESP found */
- int esp_id; /* Unique per-ESP ID number */
-
- /* For slow to medium speed input clock rates we shoot for 5mb/s,
- * but for high input clock rates we try to do 10mb/s although I
- * don't think a transfer can even run that fast with an ESP even
- * with DMA2 scatter gather pipelining.
- */
-#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */
-#define SYNC_DEFP_FAST 0x19 /* 10mb/s */
-
- unsigned int snip; /* Sync. negotiation in progress */
- unsigned int wnip; /* WIDE negotiation in progress */
- unsigned int targets_present;/* targets spoken to before */
-
- int current_transfer_size; /* Set at beginning of data dma */
-
- u8 espcmdlog[32]; /* Log of current esp cmds sent. */
- u8 espcmdent; /* Current entry in esp cmd log. */
-
- /* Misc. info about this ESP */
- enum esp_rev erev; /* ESP revision */
- int irq; /* SBus IRQ for this ESP */
- int scsi_id; /* Who am I as initiator? */
- int scsi_id_mask; /* Bitmask of 'me'. */
- int diff; /* Differential SCSI bus? */
- int bursts; /* Burst sizes our DVMA supports */
-
- /* Our command queues, only one cmd lives in the current_SC queue. */
- struct scsi_cmnd *issue_SC; /* Commands to be issued */
- struct scsi_cmnd *current_SC; /* Who is currently working the bus */
- struct scsi_cmnd *disconnected_SC;/* Commands disconnected from the bus */
-
- /* Message goo */
- u8 cur_msgout[16];
- u8 cur_msgin[16];
- u8 prevmsgout, prevmsgin;
- u8 msgout_len, msgin_len;
- u8 msgout_ctr, msgin_ctr;
-
- /* States that we cannot keep in the per cmd structure because they
- * cannot be assosciated with any specific command.
- */
- u8 resetting_bus;
- wait_queue_head_t reset_queue;
-};
-
-/* Bitfield meanings for the above registers. */
-
-/* ESP config reg 1, read-write, found on all ESP chips */
-#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */
-#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */
-#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */
-#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */
-#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */
-#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */
-
-/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */
-#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */
-#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */
-#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */
-#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */
-#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */
-#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */
-#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */
-#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */
-#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216) */
-#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */
-#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */
-#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */
-#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */
-
-/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */
-#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */
-#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */
-#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */
-#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */
-#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */
-#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */
-#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */
-#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */
-#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */
-#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */
-#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */
-#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */
-#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */
-#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */
-#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */
-#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */
-
-/* ESP command register read-write */
-/* Group 1 commands: These may be sent at any point in time to the ESP
- * chip. None of them can generate interrupts 'cept
- * the "SCSI bus reset" command if you have not disabled
- * SCSI reset interrupts in the config1 ESP register.
- */
-#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */
-#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */
-#define ESP_CMD_RC 0x02 /* Chip reset */
-#define ESP_CMD_RS 0x03 /* SCSI bus reset */
-
-/* Group 2 commands: ESP must be an initiator and connected to a target
- * for these commands to work.
- */
-#define ESP_CMD_TI 0x10 /* Transfer Information */
-#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */
-#define ESP_CMD_MOK 0x12 /* Message okie-dokie */
-#define ESP_CMD_TPAD 0x18 /* Transfer Pad */
-#define ESP_CMD_SATN 0x1a /* Set ATN */
-#define ESP_CMD_RATN 0x1b /* De-assert ATN */
-
-/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected
- * to a target as the initiator for these commands to work.
- */
-#define ESP_CMD_SMSG 0x20 /* Send message */
-#define ESP_CMD_SSTAT 0x21 /* Send status */
-#define ESP_CMD_SDATA 0x22 /* Send data */
-#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */
-#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */
-#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */
-#define ESP_CMD_DCNCT 0x27 /* Disconnect */
-#define ESP_CMD_RMSG 0x28 /* Receive Message */
-#define ESP_CMD_RCMD 0x29 /* Receive Command */
-#define ESP_CMD_RDATA 0x2a /* Receive Data */
-#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */
-
-/* Group 4 commands: The ESP must be in the disconnected state and must
- * not be connected to any targets as initiator for
- * these commands to work.
- */
-#define ESP_CMD_RSEL 0x40 /* Reselect */
-#define ESP_CMD_SEL 0x41 /* Select w/o ATN */
-#define ESP_CMD_SELA 0x42 /* Select w/ATN */
-#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */
-#define ESP_CMD_ESEL 0x44 /* Enable selection */
-#define ESP_CMD_DSEL 0x45 /* Disable selections */
-#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */
-#define ESP_CMD_RSEL3 0x47 /* Reselect3 */
-
-/* This bit enables the ESP's DMA on the SBus */
-#define ESP_CMD_DMA 0x80 /* Do DMA? */
-
-
-/* ESP status register read-only */
-#define ESP_STAT_PIO 0x01 /* IO phase bit */
-#define ESP_STAT_PCD 0x02 /* CD phase bit */
-#define ESP_STAT_PMSG 0x04 /* MSG phase bit */
-#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */
-#define ESP_STAT_TDONE 0x08 /* Transfer Completed */
-#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */
-#define ESP_STAT_PERR 0x20 /* Parity error */
-#define ESP_STAT_SPAM 0x40 /* Real bad error */
-/* This indicates the 'interrupt pending' condition on esp236, it is a reserved
- * bit on other revs of the ESP.
- */
-#define ESP_STAT_INTR 0x80 /* Interrupt */
-
-/* HME only: status 2 register */
-#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */
-#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */
-#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */
-#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */
-#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */
-#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */
-#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */
-#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */
-
-/* The status register can be masked with ESP_STAT_PMASK and compared
- * with the following values to determine the current phase the ESP
- * (at least thinks it) is in. For our purposes we also add our own
- * software 'done' bit for our phase management engine.
- */
-#define ESP_DOP (0) /* Data Out */
-#define ESP_DIP (ESP_STAT_PIO) /* Data In */
-#define ESP_CMDP (ESP_STAT_PCD) /* Command */
-#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */
-#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */
-#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */
-
-/* ESP interrupt register read-only */
-#define ESP_INTR_S 0x01 /* Select w/o ATN */
-#define ESP_INTR_SATN 0x02 /* Select w/ATN */
-#define ESP_INTR_RSEL 0x04 /* Reselected */
-#define ESP_INTR_FDONE 0x08 /* Function done */
-#define ESP_INTR_BSERV 0x10 /* Bus service */
-#define ESP_INTR_DC 0x20 /* Disconnect */
-#define ESP_INTR_IC 0x40 /* Illegal command given */
-#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */
-
-/* Interrupt status macros */
-#define ESP_SRESET_IRQ(esp) ((esp)->intreg & (ESP_INTR_SR))
-#define ESP_ILLCMD_IRQ(esp) ((esp)->intreg & (ESP_INTR_IC))
-#define ESP_SELECT_WITH_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_SATN))
-#define ESP_SELECT_WITHOUT_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_S))
-#define ESP_SELECTION_IRQ(esp) ((ESP_SELECT_WITH_ATN_IRQ(esp)) || \
- (ESP_SELECT_WITHOUT_ATN_IRQ(esp)))
-#define ESP_RESELECTION_IRQ(esp) ((esp)->intreg & (ESP_INTR_RSEL))
-
-/* ESP sequence step register read-only */
-#define ESP_STEP_VBITS 0x07 /* Valid bits */
-#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */
-#define ESP_STEP_SID 0x01 /* One msg byte sent */
-#define ESP_STEP_NCMD 0x02 /* Was not in command phase */
-#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd
- * bytes to be lost
- */
-#define ESP_STEP_FINI4 0x04 /* Command was sent ok */
-
-/* Ho hum, some ESP's set the step register to this as well... */
-#define ESP_STEP_FINI5 0x05
-#define ESP_STEP_FINI6 0x06
-#define ESP_STEP_FINI7 0x07
-
-/* ESP chip-test register read-write */
-#define ESP_TEST_TARG 0x01 /* Target test mode */
-#define ESP_TEST_INI 0x02 /* Initiator test mode */
-#define ESP_TEST_TS 0x04 /* Tristate test mode */
-
-/* ESP unique ID register read-only, found on fas236+fas100a only */
-#define ESP_UID_F100A 0x00 /* ESP FAS100A */
-#define ESP_UID_F236 0x02 /* ESP FAS236 */
-#define ESP_UID_REV 0x07 /* ESP revision */
-#define ESP_UID_FAM 0xf8 /* ESP family */
-
-/* ESP fifo flags register read-only */
-/* Note that the following implies a 16 byte FIFO on the ESP. */
-#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */
-#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */
-#define ESP_FF_SSTEP 0xe0 /* Sequence step */
-
-/* ESP clock conversion factor register write-only */
-#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */
-#define ESP_CCF_NEVER 0x01 /* Set it to this and die */
-#define ESP_CCF_F2 0x02 /* 10MHz */
-#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */
-#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */
-#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */
-#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */
-#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */
-
-/* HME only... */
-#define ESP_BUSID_RESELID 0x10
-#define ESP_BUSID_CTR32BIT 0x40
-
-#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */
-#define ESP_TIMEO_CONST 8192
-#define ESP_NEG_DEFP(mhz, cfact) \
- ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
-#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000))
-#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000))
-
-#endif /* !(_SPARC_ESP_H) */
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
new file mode 100644
index 000000000000..99ce03331b64
--- /dev/null
+++ b/drivers/scsi/esp_scsi.c
@@ -0,0 +1,2711 @@
+/* esp_scsi.c: ESP SCSI driver.
+ *
+ * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/irqreturn.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_transport_spi.h>
+
+#include "esp_scsi.h"
+
+#define DRV_MODULE_NAME "esp"
+#define PFX DRV_MODULE_NAME ": "
+#define DRV_VERSION "2.000"
+#define DRV_MODULE_RELDATE "April 19, 2007"
+
+/* SCSI bus reset settle time in seconds. */
+static int esp_bus_reset_settle = 3;
+
+static u32 esp_debug;
+#define ESP_DEBUG_INTR 0x00000001
+#define ESP_DEBUG_SCSICMD 0x00000002
+#define ESP_DEBUG_RESET 0x00000004
+#define ESP_DEBUG_MSGIN 0x00000008
+#define ESP_DEBUG_MSGOUT 0x00000010
+#define ESP_DEBUG_CMDDONE 0x00000020
+#define ESP_DEBUG_DISCONNECT 0x00000040
+#define ESP_DEBUG_DATASTART 0x00000080
+#define ESP_DEBUG_DATADONE 0x00000100
+#define ESP_DEBUG_RECONNECT 0x00000200
+#define ESP_DEBUG_AUTOSENSE 0x00000400
+
+#define esp_log_intr(f, a...) \
+do { if (esp_debug & ESP_DEBUG_INTR) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_reset(f, a...) \
+do { if (esp_debug & ESP_DEBUG_RESET) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_msgin(f, a...) \
+do { if (esp_debug & ESP_DEBUG_MSGIN) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_msgout(f, a...) \
+do { if (esp_debug & ESP_DEBUG_MSGOUT) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_cmddone(f, a...) \
+do { if (esp_debug & ESP_DEBUG_CMDDONE) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_disconnect(f, a...) \
+do { if (esp_debug & ESP_DEBUG_DISCONNECT) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_datastart(f, a...) \
+do { if (esp_debug & ESP_DEBUG_DATASTART) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_datadone(f, a...) \
+do { if (esp_debug & ESP_DEBUG_DATADONE) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_reconnect(f, a...) \
+do { if (esp_debug & ESP_DEBUG_RECONNECT) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_log_autosense(f, a...) \
+do { if (esp_debug & ESP_DEBUG_AUTOSENSE) \
+ printk(f, ## a); \
+} while (0)
+
+#define esp_read8(REG) esp->ops->esp_read8(esp, REG)
+#define esp_write8(VAL,REG) esp->ops->esp_write8(esp, VAL, REG)
+
+static void esp_log_fill_regs(struct esp *esp,
+ struct esp_event_ent *p)
+{
+ p->sreg = esp->sreg;
+ p->seqreg = esp->seqreg;
+ p->sreg2 = esp->sreg2;
+ p->ireg = esp->ireg;
+ p->select_state = esp->select_state;
+ p->event = esp->event;
+}
+
+void scsi_esp_cmd(struct esp *esp, u8 val)
+{
+ struct esp_event_ent *p;
+ int idx = esp->esp_event_cur;
+
+ p = &esp->esp_event_log[idx];
+ p->type = ESP_EVENT_TYPE_CMD;
+ p->val = val;
+ esp_log_fill_regs(esp, p);
+
+ esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
+
+ esp_write8(val, ESP_CMD);
+}
+EXPORT_SYMBOL(scsi_esp_cmd);
+
+static void esp_event(struct esp *esp, u8 val)
+{
+ struct esp_event_ent *p;
+ int idx = esp->esp_event_cur;
+
+ p = &esp->esp_event_log[idx];
+ p->type = ESP_EVENT_TYPE_EVENT;
+ p->val = val;
+ esp_log_fill_regs(esp, p);
+
+ esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
+
+ esp->event = val;
+}
+
+static void esp_dump_cmd_log(struct esp *esp)
+{
+ int idx = esp->esp_event_cur;
+ int stop = idx;
+
+ printk(KERN_INFO PFX "esp%d: Dumping command log\n",
+ esp->host->unique_id);
+ do {
+ struct esp_event_ent *p = &esp->esp_event_log[idx];
+
+ printk(KERN_INFO PFX "esp%d: ent[%d] %s ",
+ esp->host->unique_id, idx,
+ p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT");
+
+ printk("val[%02x] sreg[%02x] seqreg[%02x] "
+ "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n",
+ p->val, p->sreg, p->seqreg,
+ p->sreg2, p->ireg, p->select_state, p->event);
+
+ idx = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
+ } while (idx != stop);
+}
+
+static void esp_flush_fifo(struct esp *esp)
+{
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ if (esp->rev == ESP236) {
+ int lim = 1000;
+
+ while (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES) {
+ if (--lim == 0) {
+ printk(KERN_ALERT PFX "esp%d: ESP_FF_BYTES "
+ "will not clear!\n",
+ esp->host->unique_id);
+ break;
+ }
+ udelay(1);
+ }
+ }
+}
+
+static void hme_read_fifo(struct esp *esp)
+{
+ int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+ int idx = 0;
+
+ while (fcnt--) {
+ esp->fifo[idx++] = esp_read8(ESP_FDATA);
+ esp->fifo[idx++] = esp_read8(ESP_FDATA);
+ }
+ if (esp->sreg2 & ESP_STAT2_F1BYTE) {
+ esp_write8(0, ESP_FDATA);
+ esp->fifo[idx++] = esp_read8(ESP_FDATA);
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ }
+ esp->fifo_cnt = idx;
+}
+
+static void esp_set_all_config3(struct esp *esp, u8 val)
+{
+ int i;
+
+ for (i = 0; i < ESP_MAX_TARGET; i++)
+ esp->target[i].esp_config3 = val;
+}
+
+/* Reset the ESP chip, _not_ the SCSI bus. */
+static void esp_reset_esp(struct esp *esp)
+{
+ u8 family_code, version;
+
+ /* Now reset the ESP chip */
+ scsi_esp_cmd(esp, ESP_CMD_RC);
+ scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
+ scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
+
+ /* Reload the configuration registers */
+ esp_write8(esp->cfact, ESP_CFACT);
+
+ esp->prev_stp = 0;
+ esp_write8(esp->prev_stp, ESP_STP);
+
+ esp->prev_soff = 0;
+ esp_write8(esp->prev_soff, ESP_SOFF);
+
+ esp_write8(esp->neg_defp, ESP_TIMEO);
+
+ /* This is the only point at which it is reliable to read
+ * the ID-code for a fast ESP chip variants.
+ */
+ esp->max_period = ((35 * esp->ccycle) / 1000);
+ if (esp->rev == FAST) {
+ version = esp_read8(ESP_UID);
+ family_code = (version & 0xf8) >> 3;
+ if (family_code == 0x02)
+ esp->rev = FAS236;
+ else if (family_code == 0x0a)
+ esp->rev = FASHME; /* Version is usually '5'. */
+ else
+ esp->rev = FAS100A;
+ esp->min_period = ((4 * esp->ccycle) / 1000);
+ } else {
+ esp->min_period = ((5 * esp->ccycle) / 1000);
+ }
+ esp->max_period = (esp->max_period + 3)>>2;
+ esp->min_period = (esp->min_period + 3)>>2;
+
+ esp_write8(esp->config1, ESP_CFG1);
+ switch (esp->rev) {
+ case ESP100:
+ /* nothing to do */
+ break;
+
+ case ESP100A:
+ esp_write8(esp->config2, ESP_CFG2);
+ break;
+
+ case ESP236:
+ /* Slow 236 */
+ esp_write8(esp->config2, ESP_CFG2);
+ esp->prev_cfg3 = esp->target[0].esp_config3;
+ esp_write8(esp->prev_cfg3, ESP_CFG3);
+ break;
+
+ case FASHME:
+ esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
+ /* fallthrough... */
+
+ case FAS236:
+ /* Fast 236 or HME */
+ esp_write8(esp->config2, ESP_CFG2);
+ if (esp->rev == FASHME) {
+ u8 cfg3 = esp->target[0].esp_config3;
+
+ cfg3 |= ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH;
+ if (esp->scsi_id >= 8)
+ cfg3 |= ESP_CONFIG3_IDBIT3;
+ esp_set_all_config3(esp, cfg3);
+ } else {
+ u32 cfg3 = esp->target[0].esp_config3;
+
+ cfg3 |= ESP_CONFIG3_FCLK;
+ esp_set_all_config3(esp, cfg3);
+ }
+ esp->prev_cfg3 = esp->target[0].esp_config3;
+ esp_write8(esp->prev_cfg3, ESP_CFG3);
+ if (esp->rev == FASHME) {
+ esp->radelay = 80;
+ } else {
+ if (esp->flags & ESP_FLAG_DIFFERENTIAL)
+ esp->radelay = 0;
+ else
+ esp->radelay = 96;
+ }
+ break;
+
+ case FAS100A:
+ /* Fast 100a */
+ esp_write8(esp->config2, ESP_CFG2);
+ esp_set_all_config3(esp,
+ (esp->target[0].esp_config3 |
+ ESP_CONFIG3_FCLOCK));
+ esp->prev_cfg3 = esp->target[0].esp_config3;
+ esp_write8(esp->prev_cfg3, ESP_CFG3);
+ esp->radelay = 32;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Eat any bitrot in the chip */
+ esp_read8(ESP_INTRPT);
+ udelay(100);
+}
+
+static void esp_map_dma(struct esp *esp, struct scsi_cmnd *cmd)
+{
+ struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+ struct scatterlist *sg = cmd->request_buffer;
+ int dir = cmd->sc_data_direction;
+ int total, i;
+
+ if (dir == DMA_NONE)
+ return;
+
+ BUG_ON(cmd->use_sg == 0);
+
+ spriv->u.num_sg = esp->ops->map_sg(esp, sg,
+ cmd->use_sg, dir);
+ spriv->cur_residue = sg_dma_len(sg);
+ spriv->cur_sg = sg;
+
+ total = 0;
+ for (i = 0; i < spriv->u.num_sg; i++)
+ total += sg_dma_len(&sg[i]);
+ spriv->tot_residue = total;
+}
+
+static dma_addr_t esp_cur_dma_addr(struct esp_cmd_entry *ent,
+ struct scsi_cmnd *cmd)
+{
+ struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ return ent->sense_dma +
+ (ent->sense_ptr - cmd->sense_buffer);
+ }
+
+ return sg_dma_address(p->cur_sg) +
+ (sg_dma_len(p->cur_sg) -
+ p->cur_residue);
+}
+
+static unsigned int esp_cur_dma_len(struct esp_cmd_entry *ent,
+ struct scsi_cmnd *cmd)
+{
+ struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ return SCSI_SENSE_BUFFERSIZE -
+ (ent->sense_ptr - cmd->sense_buffer);
+ }
+ return p->cur_residue;
+}
+
+static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent,
+ struct scsi_cmnd *cmd, unsigned int len)
+{
+ struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ ent->sense_ptr += len;
+ return;
+ }
+
+ p->cur_residue -= len;
+ p->tot_residue -= len;
+ if (p->cur_residue < 0 || p->tot_residue < 0) {
+ printk(KERN_ERR PFX "esp%d: Data transfer overflow.\n",
+ esp->host->unique_id);
+ printk(KERN_ERR PFX "esp%d: cur_residue[%d] tot_residue[%d] "
+ "len[%u]\n",
+ esp->host->unique_id,
+ p->cur_residue, p->tot_residue, len);
+ p->cur_residue = 0;
+ p->tot_residue = 0;
+ }
+ if (!p->cur_residue && p->tot_residue) {
+ p->cur_sg++;
+ p->cur_residue = sg_dma_len(p->cur_sg);
+ }
+}
+
+static void esp_unmap_dma(struct esp *esp, struct scsi_cmnd *cmd)
+{
+ struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+ int dir = cmd->sc_data_direction;
+
+ if (dir == DMA_NONE)
+ return;
+
+ esp->ops->unmap_sg(esp, cmd->request_buffer,
+ spriv->u.num_sg, dir);
+}
+
+static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent)
+{
+ struct scsi_cmnd *cmd = ent->cmd;
+ struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ ent->saved_sense_ptr = ent->sense_ptr;
+ return;
+ }
+ ent->saved_cur_residue = spriv->cur_residue;
+ ent->saved_cur_sg = spriv->cur_sg;
+ ent->saved_tot_residue = spriv->tot_residue;
+}
+
+static void esp_restore_pointers(struct esp *esp, struct esp_cmd_entry *ent)
+{
+ struct scsi_cmnd *cmd = ent->cmd;
+ struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ ent->sense_ptr = ent->saved_sense_ptr;
+ return;
+ }
+ spriv->cur_residue = ent->saved_cur_residue;
+ spriv->cur_sg = ent->saved_cur_sg;
+ spriv->tot_residue = ent->saved_tot_residue;
+}
+
+static void esp_check_command_len(struct esp *esp, struct scsi_cmnd *cmd)
+{
+ if (cmd->cmd_len == 6 ||
+ cmd->cmd_len == 10 ||
+ cmd->cmd_len == 12) {
+ esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
+ } else {
+ esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+ }
+}
+
+static void esp_write_tgt_config3(struct esp *esp, int tgt)
+{
+ if (esp->rev > ESP100A) {
+ u8 val = esp->target[tgt].esp_config3;
+
+ if (val != esp->prev_cfg3) {
+ esp->prev_cfg3 = val;
+ esp_write8(val, ESP_CFG3);
+ }
+ }
+}
+
+static void esp_write_tgt_sync(struct esp *esp, int tgt)
+{
+ u8 off = esp->target[tgt].esp_offset;
+ u8 per = esp->target[tgt].esp_period;
+
+ if (off != esp->prev_soff) {
+ esp->prev_soff = off;
+ esp_write8(off, ESP_SOFF);
+ }
+ if (per != esp->prev_stp) {
+ esp->prev_stp = per;
+ esp_write8(per, ESP_STP);
+ }
+}
+
+static u32 esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
+{
+ if (esp->rev == FASHME) {
+ /* Arbitrary segment boundaries, 24-bit counts. */
+ if (dma_len > (1U << 24))
+ dma_len = (1U << 24);
+ } else {
+ u32 base, end;
+
+ /* ESP chip limits other variants by 16-bits of transfer
+ * count. Actually on FAS100A and FAS236 we could get
+ * 24-bits of transfer count by enabling ESP_CONFIG2_FENAB
+ * in the ESP_CFG2 register but that causes other unwanted
+ * changes so we don't use it currently.
+ */
+ if (dma_len > (1U << 16))
+ dma_len = (1U << 16);
+
+ /* All of the DMA variants hooked up to these chips
+ * cannot handle crossing a 24-bit address boundary.
+ */
+ base = dma_addr & ((1U << 24) - 1U);
+ end = base + dma_len;
+ if (end > (1U << 24))
+ end = (1U <<24);
+ dma_len = end - base;
+ }
+ return dma_len;
+}
+
+static int esp_need_to_nego_wide(struct esp_target_data *tp)
+{
+ struct scsi_target *target = tp->starget;
+
+ return spi_width(target) != tp->nego_goal_width;
+}
+
+static int esp_need_to_nego_sync(struct esp_target_data *tp)
+{
+ struct scsi_target *target = tp->starget;
+
+ /* When offset is zero, period is "don't care". */
+ if (!spi_offset(target) && !tp->nego_goal_offset)
+ return 0;
+
+ if (spi_offset(target) == tp->nego_goal_offset &&
+ spi_period(target) == tp->nego_goal_period)
+ return 0;
+
+ return 1;
+}
+
+static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
+ struct esp_lun_data *lp)
+{
+ if (!ent->tag[0]) {
+ /* Non-tagged, slot already taken? */
+ if (lp->non_tagged_cmd)
+ return -EBUSY;
+
+ if (lp->hold) {
+ /* We are being held by active tagged
+ * commands.
+ */
+ if (lp->num_tagged)
+ return -EBUSY;
+
+ /* Tagged commands completed, we can unplug
+ * the queue and run this untagged command.
+ */
+ lp->hold = 0;
+ } else if (lp->num_tagged) {
+ /* Plug the queue until num_tagged decreases
+ * to zero in esp_free_lun_tag.
+ */
+ lp->hold = 1;
+ return -EBUSY;
+ }
+
+ lp->non_tagged_cmd = ent;
+ return 0;
+ } else {
+ /* Tagged command, see if blocked by a
+ * non-tagged one.
+ */
+ if (lp->non_tagged_cmd || lp->hold)
+ return -EBUSY;
+ }
+
+ BUG_ON(lp->tagged_cmds[ent->tag[1]]);
+
+ lp->tagged_cmds[ent->tag[1]] = ent;
+ lp->num_tagged++;
+
+ return 0;
+}
+
+static void esp_free_lun_tag(struct esp_cmd_entry *ent,
+ struct esp_lun_data *lp)
+{
+ if (ent->tag[0]) {
+ BUG_ON(lp->tagged_cmds[ent->tag[1]] != ent);
+ lp->tagged_cmds[ent->tag[1]] = NULL;
+ lp->num_tagged--;
+ } else {
+ BUG_ON(lp->non_tagged_cmd != ent);
+ lp->non_tagged_cmd = NULL;
+ }
+}
+
+/* When a contingent allegiance conditon is created, we force feed a
+ * REQUEST_SENSE command to the device to fetch the sense data. I
+ * tried many other schemes, relying on the scsi error handling layer
+ * to send out the REQUEST_SENSE automatically, but this was difficult
+ * to get right especially in the presence of applications like smartd
+ * which use SG_IO to send out their own REQUEST_SENSE commands.
+ */
+static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent)
+{
+ struct scsi_cmnd *cmd = ent->cmd;
+ struct scsi_device *dev = cmd->device;
+ int tgt, lun;
+ u8 *p, val;
+
+ tgt = dev->id;
+ lun = dev->lun;
+
+
+ if (!ent->sense_ptr) {
+ esp_log_autosense("esp%d: Doing auto-sense for "
+ "tgt[%d] lun[%d]\n",
+ esp->host->unique_id, tgt, lun);
+
+ ent->sense_ptr = cmd->sense_buffer;
+ ent->sense_dma = esp->ops->map_single(esp,
+ ent->sense_ptr,
+ SCSI_SENSE_BUFFERSIZE,
+ DMA_FROM_DEVICE);
+ }
+ ent->saved_sense_ptr = ent->sense_ptr;
+
+ esp->active_cmd = ent;
+
+ p = esp->command_block;
+ esp->msg_out_len = 0;
+
+ *p++ = IDENTIFY(0, lun);
+ *p++ = REQUEST_SENSE;
+ *p++ = ((dev->scsi_level <= SCSI_2) ?
+ (lun << 5) : 0);
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = SCSI_SENSE_BUFFERSIZE;
+ *p++ = 0;
+
+ esp->select_state = ESP_SELECT_BASIC;
+
+ val = tgt;
+ if (esp->rev == FASHME)
+ val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT;
+ esp_write8(val, ESP_BUSID);
+
+ esp_write_tgt_sync(esp, tgt);
+ esp_write_tgt_config3(esp, tgt);
+
+ val = (p - esp->command_block);
+
+ if (esp->rev == FASHME)
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+ val, 16, 0, ESP_CMD_DMA | ESP_CMD_SELA);
+}
+
+static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
+{
+ struct esp_cmd_entry *ent;
+
+ list_for_each_entry(ent, &esp->queued_cmds, list) {
+ struct scsi_cmnd *cmd = ent->cmd;
+ struct scsi_device *dev = cmd->device;
+ struct esp_lun_data *lp = dev->hostdata;
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ ent->tag[0] = 0;
+ ent->tag[1] = 0;
+ return ent;
+ }
+
+ if (!scsi_populate_tag_msg(cmd, &ent->tag[0])) {
+ ent->tag[0] = 0;
+ ent->tag[1] = 0;
+ }
+
+ if (esp_alloc_lun_tag(ent, lp) < 0)
+ continue;
+
+ return ent;
+ }
+
+ return NULL;
+}
+
+static void esp_maybe_execute_command(struct esp *esp)
+{
+ struct esp_target_data *tp;
+ struct esp_lun_data *lp;
+ struct scsi_device *dev;
+ struct scsi_cmnd *cmd;
+ struct esp_cmd_entry *ent;
+ int tgt, lun, i;
+ u32 val, start_cmd;
+ u8 *p;
+
+ if (esp->active_cmd ||
+ (esp->flags & ESP_FLAG_RESETTING))
+ return;
+
+ ent = find_and_prep_issuable_command(esp);
+ if (!ent)
+ return;
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ esp_autosense(esp, ent);
+ return;
+ }
+
+ cmd = ent->cmd;
+ dev = cmd->device;
+ tgt = dev->id;
+ lun = dev->lun;
+ tp = &esp->target[tgt];
+ lp = dev->hostdata;
+
+ list_del(&ent->list);
+ list_add(&ent->list, &esp->active_cmds);
+
+ esp->active_cmd = ent;
+
+ esp_map_dma(esp, cmd);
+ esp_save_pointers(esp, ent);
+
+ esp_check_command_len(esp, cmd);
+
+ p = esp->command_block;
+
+ esp->msg_out_len = 0;
+ if (tp->flags & ESP_TGT_CHECK_NEGO) {
+ /* Need to negotiate. If the target is broken
+ * go for synchronous transfers and non-wide.
+ */
+ if (tp->flags & ESP_TGT_BROKEN) {
+ tp->flags &= ~ESP_TGT_DISCONNECT;
+ tp->nego_goal_period = 0;
+ tp->nego_goal_offset = 0;
+ tp->nego_goal_width = 0;
+ tp->nego_goal_tags = 0;
+ }
+
+ /* If the settings are not changing, skip this. */
+ if (spi_width(tp->starget) == tp->nego_goal_width &&
+ spi_period(tp->starget) == tp->nego_goal_period &&
+ spi_offset(tp->starget) == tp->nego_goal_offset) {
+ tp->flags &= ~ESP_TGT_CHECK_NEGO;
+ goto build_identify;
+ }
+
+ if (esp->rev == FASHME && esp_need_to_nego_wide(tp)) {
+ esp->msg_out_len =
+ spi_populate_width_msg(&esp->msg_out[0],
+ (tp->nego_goal_width ?
+ 1 : 0));
+ tp->flags |= ESP_TGT_NEGO_WIDE;
+ } else if (esp_need_to_nego_sync(tp)) {
+ esp->msg_out_len =
+ spi_populate_sync_msg(&esp->msg_out[0],
+ tp->nego_goal_period,
+ tp->nego_goal_offset);
+ tp->flags |= ESP_TGT_NEGO_SYNC;
+ } else {
+ tp->flags &= ~ESP_TGT_CHECK_NEGO;
+ }
+
+ /* Process it like a slow command. */
+ if (tp->flags & (ESP_TGT_NEGO_WIDE | ESP_TGT_NEGO_SYNC))
+ esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+ }
+
+build_identify:
+ /* If we don't have a lun-data struct yet, we're probing
+ * so do not disconnect. Also, do not disconnect unless
+ * we have a tag on this command.
+ */
+ if (lp && (tp->flags & ESP_TGT_DISCONNECT) && ent->tag[0])
+ *p++ = IDENTIFY(1, lun);
+ else
+ *p++ = IDENTIFY(0, lun);
+
+ if (ent->tag[0] && esp->rev == ESP100) {
+ /* ESP100 lacks select w/atn3 command, use select
+ * and stop instead.
+ */
+ esp->flags |= ESP_FLAG_DOING_SLOWCMD;
+ }
+
+ if (!(esp->flags & ESP_FLAG_DOING_SLOWCMD)) {
+ start_cmd = ESP_CMD_DMA | ESP_CMD_SELA;
+ if (ent->tag[0]) {
+ *p++ = ent->tag[0];
+ *p++ = ent->tag[1];
+
+ start_cmd = ESP_CMD_DMA | ESP_CMD_SA3;
+ }
+
+ for (i = 0; i < cmd->cmd_len; i++)
+ *p++ = cmd->cmnd[i];
+
+ esp->select_state = ESP_SELECT_BASIC;
+ } else {
+ esp->cmd_bytes_left = cmd->cmd_len;
+ esp->cmd_bytes_ptr = &cmd->cmnd[0];
+
+ if (ent->tag[0]) {
+ for (i = esp->msg_out_len - 1;
+ i >= 0; i--)
+ esp->msg_out[i + 2] = esp->msg_out[i];
+ esp->msg_out[0] = ent->tag[0];
+ esp->msg_out[1] = ent->tag[1];
+ esp->msg_out_len += 2;
+ }
+
+ start_cmd = ESP_CMD_DMA | ESP_CMD_SELAS;
+ esp->select_state = ESP_SELECT_MSGOUT;
+ }
+ val = tgt;
+ if (esp->rev == FASHME)
+ val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT;
+ esp_write8(val, ESP_BUSID);
+
+ esp_write_tgt_sync(esp, tgt);
+ esp_write_tgt_config3(esp, tgt);
+
+ val = (p - esp->command_block);
+
+ if (esp_debug & ESP_DEBUG_SCSICMD) {
+ printk("ESP: tgt[%d] lun[%d] scsi_cmd [ ", tgt, lun);
+ for (i = 0; i < cmd->cmd_len; i++)
+ printk("%02x ", cmd->cmnd[i]);
+ printk("]\n");
+ }
+
+ if (esp->rev == FASHME)
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+ val, 16, 0, start_cmd);
+}
+
+static struct esp_cmd_entry *esp_get_ent(struct esp *esp)
+{
+ struct list_head *head = &esp->esp_cmd_pool;
+ struct esp_cmd_entry *ret;
+
+ if (list_empty(head)) {
+ ret = kzalloc(sizeof(struct esp_cmd_entry), GFP_ATOMIC);
+ } else {
+ ret = list_entry(head->next, struct esp_cmd_entry, list);
+ list_del(&ret->list);
+ memset(ret, 0, sizeof(*ret));
+ }
+ return ret;
+}
+
+static void esp_put_ent(struct esp *esp, struct esp_cmd_entry *ent)
+{
+ list_add(&ent->list, &esp->esp_cmd_pool);
+}
+
+static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent,
+ struct scsi_cmnd *cmd, unsigned int result)
+{
+ struct scsi_device *dev = cmd->device;
+ int tgt = dev->id;
+ int lun = dev->lun;
+
+ esp->active_cmd = NULL;
+ esp_unmap_dma(esp, cmd);
+ esp_free_lun_tag(ent, dev->hostdata);
+ cmd->result = result;
+
+ if (ent->eh_done) {
+ complete(ent->eh_done);
+ ent->eh_done = NULL;
+ }
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ esp->ops->unmap_single(esp, ent->sense_dma,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ ent->sense_ptr = NULL;
+
+ /* Restore the message/status bytes to what we actually
+ * saw originally. Also, report that we are providing
+ * the sense data.
+ */
+ cmd->result = ((DRIVER_SENSE << 24) |
+ (DID_OK << 16) |
+ (COMMAND_COMPLETE << 8) |
+ (SAM_STAT_CHECK_CONDITION << 0));
+
+ ent->flags &= ~ESP_CMD_FLAG_AUTOSENSE;
+ if (esp_debug & ESP_DEBUG_AUTOSENSE) {
+ int i;
+
+ printk("esp%d: tgt[%d] lun[%d] AUTO SENSE[ ",
+ esp->host->unique_id, tgt, lun);
+ for (i = 0; i < 18; i++)
+ printk("%02x ", cmd->sense_buffer[i]);
+ printk("]\n");
+ }
+ }
+
+ cmd->scsi_done(cmd);
+
+ list_del(&ent->list);
+ esp_put_ent(esp, ent);
+
+ esp_maybe_execute_command(esp);
+}
+
+static unsigned int compose_result(unsigned int status, unsigned int message,
+ unsigned int driver_code)
+{
+ return (status | (message << 8) | (driver_code << 16));
+}
+
+static void esp_event_queue_full(struct esp *esp, struct esp_cmd_entry *ent)
+{
+ struct scsi_device *dev = ent->cmd->device;
+ struct esp_lun_data *lp = dev->hostdata;
+
+ scsi_track_queue_full(dev, lp->num_tagged - 1);
+}
+
+static int esp_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct scsi_device *dev = cmd->device;
+ struct esp *esp = host_to_esp(dev->host);
+ struct esp_cmd_priv *spriv;
+ struct esp_cmd_entry *ent;
+
+ ent = esp_get_ent(esp);
+ if (!ent)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ ent->cmd = cmd;
+
+ cmd->scsi_done = done;
+
+ spriv = ESP_CMD_PRIV(cmd);
+ spriv->u.dma_addr = ~(dma_addr_t)0x0;
+
+ list_add_tail(&ent->list, &esp->queued_cmds);
+
+ esp_maybe_execute_command(esp);
+
+ return 0;
+}
+
+static int esp_check_gross_error(struct esp *esp)
+{
+ if (esp->sreg & ESP_STAT_SPAM) {
+ /* Gross Error, could be one of:
+ * - top of fifo overwritten
+ * - top of command register overwritten
+ * - DMA programmed with wrong direction
+ * - improper phase change
+ */
+ printk(KERN_ERR PFX "esp%d: Gross error sreg[%02x]\n",
+ esp->host->unique_id, esp->sreg);
+ /* XXX Reset the chip. XXX */
+ return 1;
+ }
+ return 0;
+}
+
+static int esp_check_spur_intr(struct esp *esp)
+{
+ switch (esp->rev) {
+ case ESP100:
+ case ESP100A:
+ /* The interrupt pending bit of the status register cannot
+ * be trusted on these revisions.
+ */
+ esp->sreg &= ~ESP_STAT_INTR;
+ break;
+
+ default:
+ if (!(esp->sreg & ESP_STAT_INTR)) {
+ esp->ireg = esp_read8(ESP_INTRPT);
+ if (esp->ireg & ESP_INTR_SR)
+ return 1;
+
+ /* If the DMA is indicating interrupt pending and the
+ * ESP is not, the only possibility is a DMA error.
+ */
+ if (!esp->ops->dma_error(esp)) {
+ printk(KERN_ERR PFX "esp%d: Spurious irq, "
+ "sreg=%x.\n",
+ esp->host->unique_id, esp->sreg);
+ return -1;
+ }
+
+ printk(KERN_ERR PFX "esp%d: DMA error\n",
+ esp->host->unique_id);
+
+ /* XXX Reset the chip. XXX */
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void esp_schedule_reset(struct esp *esp)
+{
+ esp_log_reset("ESP: esp_schedule_reset() from %p\n",
+ __builtin_return_address(0));
+ esp->flags |= ESP_FLAG_RESETTING;
+ esp_event(esp, ESP_EVENT_RESET);
+}
+
+/* In order to avoid having to add a special half-reconnected state
+ * into the driver we just sit here and poll through the rest of
+ * the reselection process to get the tag message bytes.
+ */
+static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp,
+ struct esp_lun_data *lp)
+{
+ struct esp_cmd_entry *ent;
+ int i;
+
+ if (!lp->num_tagged) {
+ printk(KERN_ERR PFX "esp%d: Reconnect w/num_tagged==0\n",
+ esp->host->unique_id);
+ return NULL;
+ }
+
+ esp_log_reconnect("ESP: reconnect tag, ");
+
+ for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) {
+ if (esp->ops->irq_pending(esp))
+ break;
+ }
+ if (i == ESP_QUICKIRQ_LIMIT) {
+ printk(KERN_ERR PFX "esp%d: Reconnect IRQ1 timeout\n",
+ esp->host->unique_id);
+ return NULL;
+ }
+
+ esp->sreg = esp_read8(ESP_STATUS);
+ esp->ireg = esp_read8(ESP_INTRPT);
+
+ esp_log_reconnect("IRQ(%d:%x:%x), ",
+ i, esp->ireg, esp->sreg);
+
+ if (esp->ireg & ESP_INTR_DC) {
+ printk(KERN_ERR PFX "esp%d: Reconnect, got disconnect.\n",
+ esp->host->unique_id);
+ return NULL;
+ }
+
+ if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) {
+ printk(KERN_ERR PFX "esp%d: Reconnect, not MIP sreg[%02x].\n",
+ esp->host->unique_id, esp->sreg);
+ return NULL;
+ }
+
+ /* DMA in the tag bytes... */
+ esp->command_block[0] = 0xff;
+ esp->command_block[1] = 0xff;
+ esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+ 2, 2, 1, ESP_CMD_DMA | ESP_CMD_TI);
+
+ /* ACK the msssage. */
+ scsi_esp_cmd(esp, ESP_CMD_MOK);
+
+ for (i = 0; i < ESP_RESELECT_TAG_LIMIT; i++) {
+ if (esp->ops->irq_pending(esp)) {
+ esp->sreg = esp_read8(ESP_STATUS);
+ esp->ireg = esp_read8(ESP_INTRPT);
+ if (esp->ireg & ESP_INTR_FDONE)
+ break;
+ }
+ udelay(1);
+ }
+ if (i == ESP_RESELECT_TAG_LIMIT) {
+ printk(KERN_ERR PFX "esp%d: Reconnect IRQ2 timeout\n",
+ esp->host->unique_id);
+ return NULL;
+ }
+ esp->ops->dma_drain(esp);
+ esp->ops->dma_invalidate(esp);
+
+ esp_log_reconnect("IRQ2(%d:%x:%x) tag[%x:%x]\n",
+ i, esp->ireg, esp->sreg,
+ esp->command_block[0],
+ esp->command_block[1]);
+
+ if (esp->command_block[0] < SIMPLE_QUEUE_TAG ||
+ esp->command_block[0] > ORDERED_QUEUE_TAG) {
+ printk(KERN_ERR PFX "esp%d: Reconnect, bad tag "
+ "type %02x.\n",
+ esp->host->unique_id, esp->command_block[0]);
+ return NULL;
+ }
+
+ ent = lp->tagged_cmds[esp->command_block[1]];
+ if (!ent) {
+ printk(KERN_ERR PFX "esp%d: Reconnect, no entry for "
+ "tag %02x.\n",
+ esp->host->unique_id, esp->command_block[1]);
+ return NULL;
+ }
+
+ return ent;
+}
+
+static int esp_reconnect(struct esp *esp)
+{
+ struct esp_cmd_entry *ent;
+ struct esp_target_data *tp;
+ struct esp_lun_data *lp;
+ struct scsi_device *dev;
+ int target, lun;
+
+ BUG_ON(esp->active_cmd);
+ if (esp->rev == FASHME) {
+ /* FASHME puts the target and lun numbers directly
+ * into the fifo.
+ */
+ target = esp->fifo[0];
+ lun = esp->fifo[1] & 0x7;
+ } else {
+ u8 bits = esp_read8(ESP_FDATA);
+
+ /* Older chips put the lun directly into the fifo, but
+ * the target is given as a sample of the arbitration
+ * lines on the bus at reselection time. So we should
+ * see the ID of the ESP and the one reconnecting target
+ * set in the bitmap.
+ */
+ if (!(bits & esp->scsi_id_mask))
+ goto do_reset;
+ bits &= ~esp->scsi_id_mask;
+ if (!bits || (bits & (bits - 1)))
+ goto do_reset;
+
+ target = ffs(bits) - 1;
+ lun = (esp_read8(ESP_FDATA) & 0x7);
+
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ if (esp->rev == ESP100) {
+ u8 ireg = esp_read8(ESP_INTRPT);
+ /* This chip has a bug during reselection that can
+ * cause a spurious illegal-command interrupt, which
+ * we simply ACK here. Another possibility is a bus
+ * reset so we must check for that.
+ */
+ if (ireg & ESP_INTR_SR)
+ goto do_reset;
+ }
+ scsi_esp_cmd(esp, ESP_CMD_NULL);
+ }
+
+ esp_write_tgt_sync(esp, target);
+ esp_write_tgt_config3(esp, target);
+
+ scsi_esp_cmd(esp, ESP_CMD_MOK);
+
+ if (esp->rev == FASHME)
+ esp_write8(target | ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT,
+ ESP_BUSID);
+
+ tp = &esp->target[target];
+ dev = __scsi_device_lookup_by_target(tp->starget, lun);
+ if (!dev) {
+ printk(KERN_ERR PFX "esp%d: Reconnect, no lp "
+ "tgt[%u] lun[%u]\n",
+ esp->host->unique_id, target, lun);
+ goto do_reset;
+ }
+ lp = dev->hostdata;
+
+ ent = lp->non_tagged_cmd;
+ if (!ent) {
+ ent = esp_reconnect_with_tag(esp, lp);
+ if (!ent)
+ goto do_reset;
+ }
+
+ esp->active_cmd = ent;
+
+ if (ent->flags & ESP_CMD_FLAG_ABORT) {
+ esp->msg_out[0] = ABORT_TASK_SET;
+ esp->msg_out_len = 1;
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+ }
+
+ esp_event(esp, ESP_EVENT_CHECK_PHASE);
+ esp_restore_pointers(esp, ent);
+ esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+ return 1;
+
+do_reset:
+ esp_schedule_reset(esp);
+ return 0;
+}
+
+static int esp_finish_select(struct esp *esp)
+{
+ struct esp_cmd_entry *ent;
+ struct scsi_cmnd *cmd;
+ u8 orig_select_state;
+
+ orig_select_state = esp->select_state;
+
+ /* No longer selecting. */
+ esp->select_state = ESP_SELECT_NONE;
+
+ esp->seqreg = esp_read8(ESP_SSTEP) & ESP_STEP_VBITS;
+ ent = esp->active_cmd;
+ cmd = ent->cmd;
+
+ if (esp->ops->dma_error(esp)) {
+ /* If we see a DMA error during or as a result of selection,
+ * all bets are off.
+ */
+ esp_schedule_reset(esp);
+ esp_cmd_is_done(esp, ent, cmd, (DID_ERROR << 16));
+ return 0;
+ }
+
+ esp->ops->dma_invalidate(esp);
+
+ if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
+ struct esp_target_data *tp = &esp->target[cmd->device->id];
+
+ /* Carefully back out of the selection attempt. Release
+ * resources (such as DMA mapping & TAG) and reset state (such
+ * as message out and command delivery variables).
+ */
+ if (!(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) {
+ esp_unmap_dma(esp, cmd);
+ esp_free_lun_tag(ent, cmd->device->hostdata);
+ tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_NEGO_WIDE);
+ esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
+ esp->cmd_bytes_ptr = NULL;
+ esp->cmd_bytes_left = 0;
+ } else {
+ esp->ops->unmap_single(esp, ent->sense_dma,
+ SCSI_SENSE_BUFFERSIZE,
+ DMA_FROM_DEVICE);
+ ent->sense_ptr = NULL;
+ }
+
+ /* Now that the state is unwound properly, put back onto
+ * the issue queue. This command is no longer active.
+ */
+ list_del(&ent->list);
+ list_add(&ent->list, &esp->queued_cmds);
+ esp->active_cmd = NULL;
+
+ /* Return value ignored by caller, it directly invokes
+ * esp_reconnect().
+ */
+ return 0;
+ }
+
+ if (esp->ireg == ESP_INTR_DC) {
+ struct scsi_device *dev = cmd->device;
+
+ /* Disconnect. Make sure we re-negotiate sync and
+ * wide parameters if this target starts responding
+ * again in the future.
+ */
+ esp->target[dev->id].flags |= ESP_TGT_CHECK_NEGO;
+
+ scsi_esp_cmd(esp, ESP_CMD_ESEL);
+ esp_cmd_is_done(esp, ent, cmd, (DID_BAD_TARGET << 16));
+ return 1;
+ }
+
+ if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
+ /* Selection successful. On pre-FAST chips we have
+ * to do a NOP and possibly clean out the FIFO.
+ */
+ if (esp->rev <= ESP236) {
+ int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+
+ scsi_esp_cmd(esp, ESP_CMD_NULL);
+
+ if (!fcnt &&
+ (!esp->prev_soff ||
+ ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
+ esp_flush_fifo(esp);
+ }
+
+ /* If we are doing a slow command, negotiation, etc.
+ * we'll do the right thing as we transition to the
+ * next phase.
+ */
+ esp_event(esp, ESP_EVENT_CHECK_PHASE);
+ return 0;
+ }
+
+ printk("ESP: Unexpected selection completion ireg[%x].\n",
+ esp->ireg);
+ esp_schedule_reset(esp);
+ return 0;
+}
+
+static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent,
+ struct scsi_cmnd *cmd)
+{
+ int fifo_cnt, ecount, bytes_sent, flush_fifo;
+
+ fifo_cnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES;
+ if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE)
+ fifo_cnt <<= 1;
+
+ ecount = 0;
+ if (!(esp->sreg & ESP_STAT_TCNT)) {
+ ecount = ((unsigned int)esp_read8(ESP_TCLOW) |
+ (((unsigned int)esp_read8(ESP_TCMED)) << 8));
+ if (esp->rev == FASHME)
+ ecount |= ((unsigned int)esp_read8(FAS_RLO)) << 16;
+ }
+
+ bytes_sent = esp->data_dma_len;
+ bytes_sent -= ecount;
+
+ if (!(ent->flags & ESP_CMD_FLAG_WRITE))
+ bytes_sent -= fifo_cnt;
+
+ flush_fifo = 0;
+ if (!esp->prev_soff) {
+ /* Synchronous data transfer, always flush fifo. */
+ flush_fifo = 1;
+ } else {
+ if (esp->rev == ESP100) {
+ u32 fflags, phase;
+
+ /* ESP100 has a chip bug where in the synchronous data
+ * phase it can mistake a final long REQ pulse from the
+ * target as an extra data byte. Fun.
+ *
+ * To detect this case we resample the status register
+ * and fifo flags. If we're still in a data phase and
+ * we see spurious chunks in the fifo, we return error
+ * to the caller which should reset and set things up
+ * such that we only try future transfers to this
+ * target in synchronous mode.
+ */
+ esp->sreg = esp_read8(ESP_STATUS);
+ phase = esp->sreg & ESP_STAT_PMASK;
+ fflags = esp_read8(ESP_FFLAGS);
+
+ if ((phase == ESP_DOP &&
+ (fflags & ESP_FF_ONOTZERO)) ||
+ (phase == ESP_DIP &&
+ (fflags & ESP_FF_FBYTES)))
+ return -1;
+ }
+ if (!(ent->flags & ESP_CMD_FLAG_WRITE))
+ flush_fifo = 1;
+ }
+
+ if (flush_fifo)
+ esp_flush_fifo(esp);
+
+ return bytes_sent;
+}
+
+static void esp_setsync(struct esp *esp, struct esp_target_data *tp,
+ u8 scsi_period, u8 scsi_offset,
+ u8 esp_stp, u8 esp_soff)
+{
+ spi_period(tp->starget) = scsi_period;
+ spi_offset(tp->starget) = scsi_offset;
+ spi_width(tp->starget) = (tp->flags & ESP_TGT_WIDE) ? 1 : 0;
+
+ if (esp_soff) {
+ esp_stp &= 0x1f;
+ esp_soff |= esp->radelay;
+ if (esp->rev >= FAS236) {
+ u8 bit = ESP_CONFIG3_FSCSI;
+ if (esp->rev >= FAS100A)
+ bit = ESP_CONFIG3_FAST;
+
+ if (scsi_period < 50) {
+ if (esp->rev == FASHME)
+ esp_soff &= ~esp->radelay;
+ tp->esp_config3 |= bit;
+ } else {
+ tp->esp_config3 &= ~bit;
+ }
+ esp->prev_cfg3 = tp->esp_config3;
+ esp_write8(esp->prev_cfg3, ESP_CFG3);
+ }
+ }
+
+ tp->esp_period = esp->prev_stp = esp_stp;
+ tp->esp_offset = esp->prev_soff = esp_soff;
+
+ esp_write8(esp_soff, ESP_SOFF);
+ esp_write8(esp_stp, ESP_STP);
+
+ tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_CHECK_NEGO);
+
+ spi_display_xfer_agreement(tp->starget);
+}
+
+static void esp_msgin_reject(struct esp *esp)
+{
+ struct esp_cmd_entry *ent = esp->active_cmd;
+ struct scsi_cmnd *cmd = ent->cmd;
+ struct esp_target_data *tp;
+ int tgt;
+
+ tgt = cmd->device->id;
+ tp = &esp->target[tgt];
+
+ if (tp->flags & ESP_TGT_NEGO_WIDE) {
+ tp->flags &= ~(ESP_TGT_NEGO_WIDE | ESP_TGT_WIDE);
+
+ if (!esp_need_to_nego_sync(tp)) {
+ tp->flags &= ~ESP_TGT_CHECK_NEGO;
+ scsi_esp_cmd(esp, ESP_CMD_RATN);
+ } else {
+ esp->msg_out_len =
+ spi_populate_sync_msg(&esp->msg_out[0],
+ tp->nego_goal_period,
+ tp->nego_goal_offset);
+ tp->flags |= ESP_TGT_NEGO_SYNC;
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+ }
+ return;
+ }
+
+ if (tp->flags & ESP_TGT_NEGO_SYNC) {
+ tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_CHECK_NEGO);
+ tp->esp_period = 0;
+ tp->esp_offset = 0;
+ esp_setsync(esp, tp, 0, 0, 0, 0);
+ scsi_esp_cmd(esp, ESP_CMD_RATN);
+ return;
+ }
+
+ esp->msg_out[0] = ABORT_TASK_SET;
+ esp->msg_out_len = 1;
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+}
+
+static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
+{
+ u8 period = esp->msg_in[3];
+ u8 offset = esp->msg_in[4];
+ u8 stp;
+
+ if (!(tp->flags & ESP_TGT_NEGO_SYNC))
+ goto do_reject;
+
+ if (offset > 15)
+ goto do_reject;
+
+ if (offset) {
+ int rounded_up, one_clock;
+
+ if (period > esp->max_period) {
+ period = offset = 0;
+ goto do_sdtr;
+ }
+ if (period < esp->min_period)
+ goto do_reject;
+
+ one_clock = esp->ccycle / 1000;
+ rounded_up = (period << 2);
+ rounded_up = (rounded_up + one_clock - 1) / one_clock;
+ stp = rounded_up;
+ if (stp && esp->rev >= FAS236) {
+ if (stp >= 50)
+ stp--;
+ }
+ } else {
+ stp = 0;
+ }
+
+ esp_setsync(esp, tp, period, offset, stp, offset);
+ return;
+
+do_reject:
+ esp->msg_out[0] = MESSAGE_REJECT;
+ esp->msg_out_len = 1;
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+ return;
+
+do_sdtr:
+ tp->nego_goal_period = period;
+ tp->nego_goal_offset = offset;
+ esp->msg_out_len =
+ spi_populate_sync_msg(&esp->msg_out[0],
+ tp->nego_goal_period,
+ tp->nego_goal_offset);
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+}
+
+static void esp_msgin_wdtr(struct esp *esp, struct esp_target_data *tp)
+{
+ int size = 8 << esp->msg_in[3];
+ u8 cfg3;
+
+ if (esp->rev != FASHME)
+ goto do_reject;
+
+ if (size != 8 && size != 16)
+ goto do_reject;
+
+ if (!(tp->flags & ESP_TGT_NEGO_WIDE))
+ goto do_reject;
+
+ cfg3 = tp->esp_config3;
+ if (size == 16) {
+ tp->flags |= ESP_TGT_WIDE;
+ cfg3 |= ESP_CONFIG3_EWIDE;
+ } else {
+ tp->flags &= ~ESP_TGT_WIDE;
+ cfg3 &= ~ESP_CONFIG3_EWIDE;
+ }
+ tp->esp_config3 = cfg3;
+ esp->prev_cfg3 = cfg3;
+ esp_write8(cfg3, ESP_CFG3);
+
+ tp->flags &= ~ESP_TGT_NEGO_WIDE;
+
+ spi_period(tp->starget) = 0;
+ spi_offset(tp->starget) = 0;
+ if (!esp_need_to_nego_sync(tp)) {
+ tp->flags &= ~ESP_TGT_CHECK_NEGO;
+ scsi_esp_cmd(esp, ESP_CMD_RATN);
+ } else {
+ esp->msg_out_len =
+ spi_populate_sync_msg(&esp->msg_out[0],
+ tp->nego_goal_period,
+ tp->nego_goal_offset);
+ tp->flags |= ESP_TGT_NEGO_SYNC;
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+ }
+ return;
+
+do_reject:
+ esp->msg_out[0] = MESSAGE_REJECT;
+ esp->msg_out_len = 1;
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+}
+
+static void esp_msgin_extended(struct esp *esp)
+{
+ struct esp_cmd_entry *ent = esp->active_cmd;
+ struct scsi_cmnd *cmd = ent->cmd;
+ struct esp_target_data *tp;
+ int tgt = cmd->device->id;
+
+ tp = &esp->target[tgt];
+ if (esp->msg_in[2] == EXTENDED_SDTR) {
+ esp_msgin_sdtr(esp, tp);
+ return;
+ }
+ if (esp->msg_in[2] == EXTENDED_WDTR) {
+ esp_msgin_wdtr(esp, tp);
+ return;
+ }
+
+ printk("ESP: Unexpected extended msg type %x\n",
+ esp->msg_in[2]);
+
+ esp->msg_out[0] = ABORT_TASK_SET;
+ esp->msg_out_len = 1;
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+}
+
+/* Analyze msgin bytes received from target so far. Return non-zero
+ * if there are more bytes needed to complete the message.
+ */
+static int esp_msgin_process(struct esp *esp)
+{
+ u8 msg0 = esp->msg_in[0];
+ int len = esp->msg_in_len;
+
+ if (msg0 & 0x80) {
+ /* Identify */
+ printk("ESP: Unexpected msgin identify\n");
+ return 0;
+ }
+
+ switch (msg0) {
+ case EXTENDED_MESSAGE:
+ if (len == 1)
+ return 1;
+ if (len < esp->msg_in[1] + 2)
+ return 1;
+ esp_msgin_extended(esp);
+ return 0;
+
+ case IGNORE_WIDE_RESIDUE: {
+ struct esp_cmd_entry *ent;
+ struct esp_cmd_priv *spriv;
+ if (len == 1)
+ return 1;
+
+ if (esp->msg_in[1] != 1)
+ goto do_reject;
+
+ ent = esp->active_cmd;
+ spriv = ESP_CMD_PRIV(ent->cmd);
+
+ if (spriv->cur_residue == sg_dma_len(spriv->cur_sg)) {
+ spriv->cur_sg--;
+ spriv->cur_residue = 1;
+ } else
+ spriv->cur_residue++;
+ spriv->tot_residue++;
+ return 0;
+ }
+ case NOP:
+ return 0;
+ case RESTORE_POINTERS:
+ esp_restore_pointers(esp, esp->active_cmd);
+ return 0;
+ case SAVE_POINTERS:
+ esp_save_pointers(esp, esp->active_cmd);
+ return 0;
+
+ case COMMAND_COMPLETE:
+ case DISCONNECT: {
+ struct esp_cmd_entry *ent = esp->active_cmd;
+
+ ent->message = msg0;
+ esp_event(esp, ESP_EVENT_FREE_BUS);
+ esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+ return 0;
+ }
+ case MESSAGE_REJECT:
+ esp_msgin_reject(esp);
+ return 0;
+
+ default:
+ do_reject:
+ esp->msg_out[0] = MESSAGE_REJECT;
+ esp->msg_out_len = 1;
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+ return 0;
+ }
+}
+
+static int esp_process_event(struct esp *esp)
+{
+ int write;
+
+again:
+ write = 0;
+ switch (esp->event) {
+ case ESP_EVENT_CHECK_PHASE:
+ switch (esp->sreg & ESP_STAT_PMASK) {
+ case ESP_DOP:
+ esp_event(esp, ESP_EVENT_DATA_OUT);
+ break;
+ case ESP_DIP:
+ esp_event(esp, ESP_EVENT_DATA_IN);
+ break;
+ case ESP_STATP:
+ esp_flush_fifo(esp);
+ scsi_esp_cmd(esp, ESP_CMD_ICCSEQ);
+ esp_event(esp, ESP_EVENT_STATUS);
+ esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+ return 1;
+
+ case ESP_MOP:
+ esp_event(esp, ESP_EVENT_MSGOUT);
+ break;
+
+ case ESP_MIP:
+ esp_event(esp, ESP_EVENT_MSGIN);
+ break;
+
+ case ESP_CMDP:
+ esp_event(esp, ESP_EVENT_CMD_START);
+ break;
+
+ default:
+ printk("ESP: Unexpected phase, sreg=%02x\n",
+ esp->sreg);
+ esp_schedule_reset(esp);
+ return 0;
+ }
+ goto again;
+ break;
+
+ case ESP_EVENT_DATA_IN:
+ write = 1;
+ /* fallthru */
+
+ case ESP_EVENT_DATA_OUT: {
+ struct esp_cmd_entry *ent = esp->active_cmd;
+ struct scsi_cmnd *cmd = ent->cmd;
+ dma_addr_t dma_addr = esp_cur_dma_addr(ent, cmd);
+ unsigned int dma_len = esp_cur_dma_len(ent, cmd);
+
+ if (esp->rev == ESP100)
+ scsi_esp_cmd(esp, ESP_CMD_NULL);
+
+ if (write)
+ ent->flags |= ESP_CMD_FLAG_WRITE;
+ else
+ ent->flags &= ~ESP_CMD_FLAG_WRITE;
+
+ dma_len = esp_dma_length_limit(esp, dma_addr, dma_len);
+ esp->data_dma_len = dma_len;
+
+ if (!dma_len) {
+ printk(KERN_ERR PFX "esp%d: DMA length is zero!\n",
+ esp->host->unique_id);
+ printk(KERN_ERR PFX "esp%d: cur adr[%08llx] len[%08x]\n",
+ esp->host->unique_id,
+ (unsigned long long)esp_cur_dma_addr(ent, cmd),
+ esp_cur_dma_len(ent, cmd));
+ esp_schedule_reset(esp);
+ return 0;
+ }
+
+ esp_log_datastart("ESP: start data addr[%08llx] len[%u] "
+ "write(%d)\n",
+ (unsigned long long)dma_addr, dma_len, write);
+
+ esp->ops->send_dma_cmd(esp, dma_addr, dma_len, dma_len,
+ write, ESP_CMD_DMA | ESP_CMD_TI);
+ esp_event(esp, ESP_EVENT_DATA_DONE);
+ break;
+ }
+ case ESP_EVENT_DATA_DONE: {
+ struct esp_cmd_entry *ent = esp->active_cmd;
+ struct scsi_cmnd *cmd = ent->cmd;
+ int bytes_sent;
+
+ if (esp->ops->dma_error(esp)) {
+ printk("ESP: data done, DMA error, resetting\n");
+ esp_schedule_reset(esp);
+ return 0;
+ }
+
+ if (ent->flags & ESP_CMD_FLAG_WRITE) {
+ /* XXX parity errors, etc. XXX */
+
+ esp->ops->dma_drain(esp);
+ }
+ esp->ops->dma_invalidate(esp);
+
+ if (esp->ireg != ESP_INTR_BSERV) {
+ /* We should always see exactly a bus-service
+ * interrupt at the end of a successful transfer.
+ */
+ printk("ESP: data done, not BSERV, resetting\n");
+ esp_schedule_reset(esp);
+ return 0;
+ }
+
+ bytes_sent = esp_data_bytes_sent(esp, ent, cmd);
+
+ esp_log_datadone("ESP: data done flgs[%x] sent[%d]\n",
+ ent->flags, bytes_sent);
+
+ if (bytes_sent < 0) {
+ /* XXX force sync mode for this target XXX */
+ esp_schedule_reset(esp);
+ return 0;
+ }
+
+ esp_advance_dma(esp, ent, cmd, bytes_sent);
+ esp_event(esp, ESP_EVENT_CHECK_PHASE);
+ goto again;
+ break;
+ }
+
+ case ESP_EVENT_STATUS: {
+ struct esp_cmd_entry *ent = esp->active_cmd;
+
+ if (esp->ireg & ESP_INTR_FDONE) {
+ ent->status = esp_read8(ESP_FDATA);
+ ent->message = esp_read8(ESP_FDATA);
+ scsi_esp_cmd(esp, ESP_CMD_MOK);
+ } else if (esp->ireg == ESP_INTR_BSERV) {
+ ent->status = esp_read8(ESP_FDATA);
+ ent->message = 0xff;
+ esp_event(esp, ESP_EVENT_MSGIN);
+ return 0;
+ }
+
+ if (ent->message != COMMAND_COMPLETE) {
+ printk("ESP: Unexpected message %x in status\n",
+ ent->message);
+ esp_schedule_reset(esp);
+ return 0;
+ }
+
+ esp_event(esp, ESP_EVENT_FREE_BUS);
+ esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+ break;
+ }
+ case ESP_EVENT_FREE_BUS: {
+ struct esp_cmd_entry *ent = esp->active_cmd;
+ struct scsi_cmnd *cmd = ent->cmd;
+
+ if (ent->message == COMMAND_COMPLETE ||
+ ent->message == DISCONNECT)
+ scsi_esp_cmd(esp, ESP_CMD_ESEL);
+
+ if (ent->message == COMMAND_COMPLETE) {
+ esp_log_cmddone("ESP: Command done status[%x] "
+ "message[%x]\n",
+ ent->status, ent->message);
+ if (ent->status == SAM_STAT_TASK_SET_FULL)
+ esp_event_queue_full(esp, ent);
+
+ if (ent->status == SAM_STAT_CHECK_CONDITION &&
+ !(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) {
+ ent->flags |= ESP_CMD_FLAG_AUTOSENSE;
+ esp_autosense(esp, ent);
+ } else {
+ esp_cmd_is_done(esp, ent, cmd,
+ compose_result(ent->status,
+ ent->message,
+ DID_OK));
+ }
+ } else if (ent->message == DISCONNECT) {
+ esp_log_disconnect("ESP: Disconnecting tgt[%d] "
+ "tag[%x:%x]\n",
+ cmd->device->id,
+ ent->tag[0], ent->tag[1]);
+
+ esp->active_cmd = NULL;
+ esp_maybe_execute_command(esp);
+ } else {
+ printk("ESP: Unexpected message %x in freebus\n",
+ ent->message);
+ esp_schedule_reset(esp);
+ return 0;
+ }
+ if (esp->active_cmd)
+ esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+ break;
+ }
+ case ESP_EVENT_MSGOUT: {
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+
+ if (esp_debug & ESP_DEBUG_MSGOUT) {
+ int i;
+ printk("ESP: Sending message [ ");
+ for (i = 0; i < esp->msg_out_len; i++)
+ printk("%02x ", esp->msg_out[i]);
+ printk("]\n");
+ }
+
+ if (esp->rev == FASHME) {
+ int i;
+
+ /* Always use the fifo. */
+ for (i = 0; i < esp->msg_out_len; i++) {
+ esp_write8(esp->msg_out[i], ESP_FDATA);
+ esp_write8(0, ESP_FDATA);
+ }
+ scsi_esp_cmd(esp, ESP_CMD_TI);
+ } else {
+ if (esp->msg_out_len == 1) {
+ esp_write8(esp->msg_out[0], ESP_FDATA);
+ scsi_esp_cmd(esp, ESP_CMD_TI);
+ } else {
+ /* Use DMA. */
+ memcpy(esp->command_block,
+ esp->msg_out,
+ esp->msg_out_len);
+
+ esp->ops->send_dma_cmd(esp,
+ esp->command_block_dma,
+ esp->msg_out_len,
+ esp->msg_out_len,
+ 0,
+ ESP_CMD_DMA|ESP_CMD_TI);
+ }
+ }
+ esp_event(esp, ESP_EVENT_MSGOUT_DONE);
+ break;
+ }
+ case ESP_EVENT_MSGOUT_DONE:
+ if (esp->rev == FASHME) {
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ } else {
+ if (esp->msg_out_len > 1)
+ esp->ops->dma_invalidate(esp);
+ }
+
+ if (!(esp->ireg & ESP_INTR_DC)) {
+ if (esp->rev != FASHME)
+ scsi_esp_cmd(esp, ESP_CMD_NULL);
+ }
+ esp_event(esp, ESP_EVENT_CHECK_PHASE);
+ goto again;
+ case ESP_EVENT_MSGIN:
+ if (esp->ireg & ESP_INTR_BSERV) {
+ if (esp->rev == FASHME) {
+ if (!(esp_read8(ESP_STATUS2) &
+ ESP_STAT2_FEMPTY))
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ } else {
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ if (esp->rev == ESP100)
+ scsi_esp_cmd(esp, ESP_CMD_NULL);
+ }
+ scsi_esp_cmd(esp, ESP_CMD_TI);
+ esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+ return 1;
+ }
+ if (esp->ireg & ESP_INTR_FDONE) {
+ u8 val;
+
+ if (esp->rev == FASHME)
+ val = esp->fifo[0];
+ else
+ val = esp_read8(ESP_FDATA);
+ esp->msg_in[esp->msg_in_len++] = val;
+
+ esp_log_msgin("ESP: Got msgin byte %x\n", val);
+
+ if (!esp_msgin_process(esp))
+ esp->msg_in_len = 0;
+
+ if (esp->rev == FASHME)
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+
+ scsi_esp_cmd(esp, ESP_CMD_MOK);
+
+ if (esp->event != ESP_EVENT_FREE_BUS)
+ esp_event(esp, ESP_EVENT_CHECK_PHASE);
+ } else {
+ printk("ESP: MSGIN neither BSERV not FDON, resetting");
+ esp_schedule_reset(esp);
+ return 0;
+ }
+ break;
+ case ESP_EVENT_CMD_START:
+ memcpy(esp->command_block, esp->cmd_bytes_ptr,
+ esp->cmd_bytes_left);
+ if (esp->rev == FASHME)
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+ esp->cmd_bytes_left, 16, 0,
+ ESP_CMD_DMA | ESP_CMD_TI);
+ esp_event(esp, ESP_EVENT_CMD_DONE);
+ esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
+ break;
+ case ESP_EVENT_CMD_DONE:
+ esp->ops->dma_invalidate(esp);
+ if (esp->ireg & ESP_INTR_BSERV) {
+ esp_event(esp, ESP_EVENT_CHECK_PHASE);
+ goto again;
+ }
+ esp_schedule_reset(esp);
+ return 0;
+ break;
+
+ case ESP_EVENT_RESET:
+ scsi_esp_cmd(esp, ESP_CMD_RS);
+ break;
+
+ default:
+ printk("ESP: Unexpected event %x, resetting\n",
+ esp->event);
+ esp_schedule_reset(esp);
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+static void esp_reset_cleanup_one(struct esp *esp, struct esp_cmd_entry *ent)
+{
+ struct scsi_cmnd *cmd = ent->cmd;
+
+ esp_unmap_dma(esp, cmd);
+ esp_free_lun_tag(ent, cmd->device->hostdata);
+ cmd->result = DID_RESET << 16;
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ esp->ops->unmap_single(esp, ent->sense_dma,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ ent->sense_ptr = NULL;
+ }
+
+ cmd->scsi_done(cmd);
+ list_del(&ent->list);
+ esp_put_ent(esp, ent);
+}
+
+static void esp_clear_hold(struct scsi_device *dev, void *data)
+{
+ struct esp_lun_data *lp = dev->hostdata;
+
+ BUG_ON(lp->num_tagged);
+ lp->hold = 0;
+}
+
+static void esp_reset_cleanup(struct esp *esp)
+{
+ struct esp_cmd_entry *ent, *tmp;
+ int i;
+
+ list_for_each_entry_safe(ent, tmp, &esp->queued_cmds, list) {
+ struct scsi_cmnd *cmd = ent->cmd;
+
+ list_del(&ent->list);
+ cmd->result = DID_RESET << 16;
+ cmd->scsi_done(cmd);
+ esp_put_ent(esp, ent);
+ }
+
+ list_for_each_entry_safe(ent, tmp, &esp->active_cmds, list) {
+ if (ent == esp->active_cmd)
+ esp->active_cmd = NULL;
+ esp_reset_cleanup_one(esp, ent);
+ }
+
+ BUG_ON(esp->active_cmd != NULL);
+
+ /* Force renegotiation of sync/wide transfers. */
+ for (i = 0; i < ESP_MAX_TARGET; i++) {
+ struct esp_target_data *tp = &esp->target[i];
+
+ tp->esp_period = 0;
+ tp->esp_offset = 0;
+ tp->esp_config3 &= ~(ESP_CONFIG3_EWIDE |
+ ESP_CONFIG3_FSCSI |
+ ESP_CONFIG3_FAST);
+ tp->flags &= ~ESP_TGT_WIDE;
+ tp->flags |= ESP_TGT_CHECK_NEGO;
+
+ if (tp->starget)
+ starget_for_each_device(tp->starget, NULL,
+ esp_clear_hold);
+ }
+}
+
+/* Runs under host->lock */
+static void __esp_interrupt(struct esp *esp)
+{
+ int finish_reset, intr_done;
+ u8 phase;
+
+ esp->sreg = esp_read8(ESP_STATUS);
+
+ if (esp->flags & ESP_FLAG_RESETTING) {
+ finish_reset = 1;
+ } else {
+ if (esp_check_gross_error(esp))
+ return;
+
+ finish_reset = esp_check_spur_intr(esp);
+ if (finish_reset < 0)
+ return;
+ }
+
+ esp->ireg = esp_read8(ESP_INTRPT);
+
+ if (esp->ireg & ESP_INTR_SR)
+ finish_reset = 1;
+
+ if (finish_reset) {
+ esp_reset_cleanup(esp);
+ if (esp->eh_reset) {
+ complete(esp->eh_reset);
+ esp->eh_reset = NULL;
+ }
+ return;
+ }
+
+ phase = (esp->sreg & ESP_STAT_PMASK);
+ if (esp->rev == FASHME) {
+ if (((phase != ESP_DIP && phase != ESP_DOP) &&
+ esp->select_state == ESP_SELECT_NONE &&
+ esp->event != ESP_EVENT_STATUS &&
+ esp->event != ESP_EVENT_DATA_DONE) ||
+ (esp->ireg & ESP_INTR_RSEL)) {
+ esp->sreg2 = esp_read8(ESP_STATUS2);
+ if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
+ (esp->sreg2 & ESP_STAT2_F1BYTE))
+ hme_read_fifo(esp);
+ }
+ }
+
+ esp_log_intr("ESP: intr sreg[%02x] seqreg[%02x] "
+ "sreg2[%02x] ireg[%02x]\n",
+ esp->sreg, esp->seqreg, esp->sreg2, esp->ireg);
+
+ intr_done = 0;
+
+ if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN | ESP_INTR_IC)) {
+ printk("ESP: unexpected IREG %02x\n", esp->ireg);
+ if (esp->ireg & ESP_INTR_IC)
+ esp_dump_cmd_log(esp);
+
+ esp_schedule_reset(esp);
+ } else {
+ if (!(esp->ireg & ESP_INTR_RSEL)) {
+ /* Some combination of FDONE, BSERV, DC. */
+ if (esp->select_state != ESP_SELECT_NONE)
+ intr_done = esp_finish_select(esp);
+ } else if (esp->ireg & ESP_INTR_RSEL) {
+ if (esp->active_cmd)
+ (void) esp_finish_select(esp);
+ intr_done = esp_reconnect(esp);
+ }
+ }
+ while (!intr_done)
+ intr_done = esp_process_event(esp);
+}
+
+irqreturn_t scsi_esp_intr(int irq, void *dev_id)
+{
+ struct esp *esp = dev_id;
+ unsigned long flags;
+ irqreturn_t ret;
+
+ spin_lock_irqsave(esp->host->host_lock, flags);
+ ret = IRQ_NONE;
+ if (esp->ops->irq_pending(esp)) {
+ ret = IRQ_HANDLED;
+ for (;;) {
+ int i;
+
+ __esp_interrupt(esp);
+ if (!(esp->flags & ESP_FLAG_QUICKIRQ_CHECK))
+ break;
+ esp->flags &= ~ESP_FLAG_QUICKIRQ_CHECK;
+
+ for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) {
+ if (esp->ops->irq_pending(esp))
+ break;
+ }
+ if (i == ESP_QUICKIRQ_LIMIT)
+ break;
+ }
+ }
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(scsi_esp_intr);
+
+static void __devinit esp_get_revision(struct esp *esp)
+{
+ u8 val;
+
+ esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
+ esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+ esp_write8(esp->config2, ESP_CFG2);
+
+ val = esp_read8(ESP_CFG2);
+ val &= ~ESP_CONFIG2_MAGIC;
+ if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+ /* If what we write to cfg2 does not come back, cfg2 is not
+ * implemented, therefore this must be a plain esp100.
+ */
+ esp->rev = ESP100;
+ } else {
+ esp->config2 = 0;
+ esp_set_all_config3(esp, 5);
+ esp->prev_cfg3 = 5;
+ esp_write8(esp->config2, ESP_CFG2);
+ esp_write8(0, ESP_CFG3);
+ esp_write8(esp->prev_cfg3, ESP_CFG3);
+
+ val = esp_read8(ESP_CFG3);
+ if (val != 5) {
+ /* The cfg2 register is implemented, however
+ * cfg3 is not, must be esp100a.
+ */
+ esp->rev = ESP100A;
+ } else {
+ esp_set_all_config3(esp, 0);
+ esp->prev_cfg3 = 0;
+ esp_write8(esp->prev_cfg3, ESP_CFG3);
+
+ /* All of cfg{1,2,3} implemented, must be one of
+ * the fas variants, figure out which one.
+ */
+ if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) {
+ esp->rev = FAST;
+ esp->sync_defp = SYNC_DEFP_FAST;
+ } else {
+ esp->rev = ESP236;
+ }
+ esp->config2 = 0;
+ esp_write8(esp->config2, ESP_CFG2);
+ }
+ }
+}
+
+static void __devinit esp_init_swstate(struct esp *esp)
+{
+ int i;
+
+ INIT_LIST_HEAD(&esp->queued_cmds);
+ INIT_LIST_HEAD(&esp->active_cmds);
+ INIT_LIST_HEAD(&esp->esp_cmd_pool);
+
+ /* Start with a clear state, domain validation (via ->slave_configure,
+ * spi_dv_device()) will attempt to enable SYNC, WIDE, and tagged
+ * commands.
+ */
+ for (i = 0 ; i < ESP_MAX_TARGET; i++) {
+ esp->target[i].flags = 0;
+ esp->target[i].nego_goal_period = 0;
+ esp->target[i].nego_goal_offset = 0;
+ esp->target[i].nego_goal_width = 0;
+ esp->target[i].nego_goal_tags = 0;
+ }
+}
+
+/* This places the ESP into a known state at boot time. */
+static void __devinit esp_bootup_reset(struct esp *esp)
+{
+ u8 val;
+
+ /* Reset the DMA */
+ esp->ops->reset_dma(esp);
+
+ /* Reset the ESP */
+ esp_reset_esp(esp);
+
+ /* Reset the SCSI bus, but tell ESP not to generate an irq */
+ val = esp_read8(ESP_CFG1);
+ val |= ESP_CONFIG1_SRRDISAB;
+ esp_write8(val, ESP_CFG1);
+
+ scsi_esp_cmd(esp, ESP_CMD_RS);
+ udelay(400);
+
+ esp_write8(esp->config1, ESP_CFG1);
+
+ /* Eat any bitrot in the chip and we are done... */
+ esp_read8(ESP_INTRPT);
+}
+
+static void __devinit esp_set_clock_params(struct esp *esp)
+{
+ int fmhz;
+ u8 ccf;
+
+ /* This is getting messy but it has to be done correctly or else
+ * you get weird behavior all over the place. We are trying to
+ * basically figure out three pieces of information.
+ *
+ * a) Clock Conversion Factor
+ *
+ * This is a representation of the input crystal clock frequency
+ * going into the ESP on this machine. Any operation whose timing
+ * is longer than 400ns depends on this value being correct. For
+ * example, you'll get blips for arbitration/selection during high
+ * load or with multiple targets if this is not set correctly.
+ *
+ * b) Selection Time-Out
+ *
+ * The ESP isn't very bright and will arbitrate for the bus and try
+ * to select a target forever if you let it. This value tells the
+ * ESP when it has taken too long to negotiate and that it should
+ * interrupt the CPU so we can see what happened. The value is
+ * computed as follows (from NCR/Symbios chip docs).
+ *
+ * (Time Out Period) * (Input Clock)
+ * STO = ----------------------------------
+ * (8192) * (Clock Conversion Factor)
+ *
+ * We use a time out period of 250ms (ESP_BUS_TIMEOUT).
+ *
+ * c) Imperical constants for synchronous offset and transfer period
+ * register values
+ *
+ * This entails the smallest and largest sync period we could ever
+ * handle on this ESP.
+ */
+ fmhz = esp->cfreq;
+
+ ccf = ((fmhz / 1000000) + 4) / 5;
+ if (ccf == 1)
+ ccf = 2;
+
+ /* If we can't find anything reasonable, just assume 20MHZ.
+ * This is the clock frequency of the older sun4c's where I've
+ * been unable to find the clock-frequency PROM property. All
+ * other machines provide useful values it seems.
+ */
+ if (fmhz <= 5000000 || ccf < 1 || ccf > 8) {
+ fmhz = 20000000;
+ ccf = 4;
+ }
+
+ esp->cfact = (ccf == 8 ? 0 : ccf);
+ esp->cfreq = fmhz;
+ esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
+ esp->ctick = ESP_TICK(ccf, esp->ccycle);
+ esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
+ esp->sync_defp = SYNC_DEFP_SLOW;
+}
+
+static const char *esp_chip_names[] = {
+ "ESP100",
+ "ESP100A",
+ "ESP236",
+ "FAS236",
+ "FAS100A",
+ "FAST",
+ "FASHME",
+};
+
+static struct scsi_transport_template *esp_transport_template;
+
+int __devinit scsi_esp_register(struct esp *esp, struct device *dev)
+{
+ static int instance;
+ int err;
+
+ esp->host->transportt = esp_transport_template;
+ esp->host->max_lun = ESP_MAX_LUN;
+ esp->host->cmd_per_lun = 2;
+
+ esp_set_clock_params(esp);
+
+ esp_get_revision(esp);
+
+ esp_init_swstate(esp);
+
+ esp_bootup_reset(esp);
+
+ printk(KERN_INFO PFX "esp%u, regs[%1p:%1p] irq[%u]\n",
+ esp->host->unique_id, esp->regs, esp->dma_regs,
+ esp->host->irq);
+ printk(KERN_INFO PFX "esp%u is a %s, %u MHz (ccf=%u), SCSI ID %u\n",
+ esp->host->unique_id, esp_chip_names[esp->rev],
+ esp->cfreq / 1000000, esp->cfact, esp->scsi_id);
+
+ /* Let the SCSI bus reset settle. */
+ ssleep(esp_bus_reset_settle);
+
+ err = scsi_add_host(esp->host, dev);
+ if (err)
+ return err;
+
+ esp->host->unique_id = instance++;
+
+ scsi_scan_host(esp->host);
+
+ return 0;
+}
+EXPORT_SYMBOL(scsi_esp_register);
+
+void __devexit scsi_esp_unregister(struct esp *esp)
+{
+ scsi_remove_host(esp->host);
+}
+EXPORT_SYMBOL(scsi_esp_unregister);
+
+static int esp_slave_alloc(struct scsi_device *dev)
+{
+ struct esp *esp = host_to_esp(dev->host);
+ struct esp_target_data *tp = &esp->target[dev->id];
+ struct esp_lun_data *lp;
+
+ lp = kzalloc(sizeof(*lp), GFP_KERNEL);
+ if (!lp)
+ return -ENOMEM;
+ dev->hostdata = lp;
+
+ tp->starget = dev->sdev_target;
+
+ spi_min_period(tp->starget) = esp->min_period;
+ spi_max_offset(tp->starget) = 15;
+
+ if (esp->flags & ESP_FLAG_WIDE_CAPABLE)
+ spi_max_width(tp->starget) = 1;
+ else
+ spi_max_width(tp->starget) = 0;
+
+ return 0;
+}
+
+static int esp_slave_configure(struct scsi_device *dev)
+{
+ struct esp *esp = host_to_esp(dev->host);
+ struct esp_target_data *tp = &esp->target[dev->id];
+ int goal_tags, queue_depth;
+
+ goal_tags = 0;
+
+ if (dev->tagged_supported) {
+ /* XXX make this configurable somehow XXX */
+ goal_tags = ESP_DEFAULT_TAGS;
+
+ if (goal_tags > ESP_MAX_TAG)
+ goal_tags = ESP_MAX_TAG;
+ }
+
+ queue_depth = goal_tags;
+ if (queue_depth < dev->host->cmd_per_lun)
+ queue_depth = dev->host->cmd_per_lun;
+
+ if (goal_tags) {
+ scsi_set_tag_type(dev, MSG_ORDERED_TAG);
+ scsi_activate_tcq(dev, queue_depth);
+ } else {
+ scsi_deactivate_tcq(dev, queue_depth);
+ }
+ tp->flags |= ESP_TGT_DISCONNECT;
+
+ if (!spi_initial_dv(dev->sdev_target))
+ spi_dv_device(dev);
+
+ return 0;
+}
+
+static void esp_slave_destroy(struct scsi_device *dev)
+{
+ struct esp_lun_data *lp = dev->hostdata;
+
+ kfree(lp);
+ dev->hostdata = NULL;
+}
+
+static int esp_eh_abort_handler(struct scsi_cmnd *cmd)
+{
+ struct esp *esp = host_to_esp(cmd->device->host);
+ struct esp_cmd_entry *ent, *tmp;
+ struct completion eh_done;
+ unsigned long flags;
+
+ /* XXX This helps a lot with debugging but might be a bit
+ * XXX much for the final driver.
+ */
+ spin_lock_irqsave(esp->host->host_lock, flags);
+ printk(KERN_ERR PFX "esp%d: Aborting command [%p:%02x]\n",
+ esp->host->unique_id, cmd, cmd->cmnd[0]);
+ ent = esp->active_cmd;
+ if (ent)
+ printk(KERN_ERR PFX "esp%d: Current command [%p:%02x]\n",
+ esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ list_for_each_entry(ent, &esp->queued_cmds, list) {
+ printk(KERN_ERR PFX "esp%d: Queued command [%p:%02x]\n",
+ esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ }
+ list_for_each_entry(ent, &esp->active_cmds, list) {
+ printk(KERN_ERR PFX "esp%d: Active command [%p:%02x]\n",
+ esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ }
+ esp_dump_cmd_log(esp);
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+ spin_lock_irqsave(esp->host->host_lock, flags);
+
+ ent = NULL;
+ list_for_each_entry(tmp, &esp->queued_cmds, list) {
+ if (tmp->cmd == cmd) {
+ ent = tmp;
+ break;
+ }
+ }
+
+ if (ent) {
+ /* Easiest case, we didn't even issue the command
+ * yet so it is trivial to abort.
+ */
+ list_del(&ent->list);
+
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+
+ esp_put_ent(esp, ent);
+
+ goto out_success;
+ }
+
+ init_completion(&eh_done);
+
+ ent = esp->active_cmd;
+ if (ent && ent->cmd == cmd) {
+ /* Command is the currently active command on
+ * the bus. If we already have an output message
+ * pending, no dice.
+ */
+ if (esp->msg_out_len)
+ goto out_failure;
+
+ /* Send out an abort, encouraging the target to
+ * go to MSGOUT phase by asserting ATN.
+ */
+ esp->msg_out[0] = ABORT_TASK_SET;
+ esp->msg_out_len = 1;
+ ent->eh_done = &eh_done;
+
+ scsi_esp_cmd(esp, ESP_CMD_SATN);
+ } else {
+ /* The command is disconnected. This is not easy to
+ * abort. For now we fail and let the scsi error
+ * handling layer go try a scsi bus reset or host
+ * reset.
+ *
+ * What we could do is put together a scsi command
+ * solely for the purpose of sending an abort message
+ * to the target. Coming up with all the code to
+ * cook up scsi commands, special case them everywhere,
+ * etc. is for questionable gain and it would be better
+ * if the generic scsi error handling layer could do at
+ * least some of that for us.
+ *
+ * Anyways this is an area for potential future improvement
+ * in this driver.
+ */
+ goto out_failure;
+ }
+
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+ if (!wait_for_completion_timeout(&eh_done, 5 * HZ)) {
+ spin_lock_irqsave(esp->host->host_lock, flags);
+ ent->eh_done = NULL;
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+ return FAILED;
+ }
+
+ return SUCCESS;
+
+out_success:
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+ return SUCCESS;
+
+out_failure:
+ /* XXX This might be a good location to set ESP_TGT_BROKEN
+ * XXX since we know which target/lun in particular is
+ * XXX causing trouble.
+ */
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+ return FAILED;
+}
+
+static int esp_eh_bus_reset_handler(struct scsi_cmnd *cmd)
+{
+ struct esp *esp = host_to_esp(cmd->device->host);
+ struct completion eh_reset;
+ unsigned long flags;
+
+ init_completion(&eh_reset);
+
+ spin_lock_irqsave(esp->host->host_lock, flags);
+
+ esp->eh_reset = &eh_reset;
+
+ /* XXX This is too simple... We should add lots of
+ * XXX checks here so that if we find that the chip is
+ * XXX very wedged we return failure immediately so
+ * XXX that we can perform a full chip reset.
+ */
+ esp->flags |= ESP_FLAG_RESETTING;
+ scsi_esp_cmd(esp, ESP_CMD_RS);
+
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+ ssleep(esp_bus_reset_settle);
+
+ if (!wait_for_completion_timeout(&eh_reset, 5 * HZ)) {
+ spin_lock_irqsave(esp->host->host_lock, flags);
+ esp->eh_reset = NULL;
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+ return FAILED;
+ }
+
+ return SUCCESS;
+}
+
+/* All bets are off, reset the entire device. */
+static int esp_eh_host_reset_handler(struct scsi_cmnd *cmd)
+{
+ struct esp *esp = host_to_esp(cmd->device->host);
+ unsigned long flags;
+
+ spin_lock_irqsave(esp->host->host_lock, flags);
+ esp_bootup_reset(esp);
+ esp_reset_cleanup(esp);
+ spin_unlock_irqrestore(esp->host->host_lock, flags);
+
+ ssleep(esp_bus_reset_settle);
+
+ return SUCCESS;
+}
+
+static const char *esp_info(struct Scsi_Host *host)
+{
+ return "esp";
+}
+
+struct scsi_host_template scsi_esp_template = {
+ .module = THIS_MODULE,
+ .name = "esp",
+ .info = esp_info,
+ .queuecommand = esp_queuecommand,
+ .slave_alloc = esp_slave_alloc,
+ .slave_configure = esp_slave_configure,
+ .slave_destroy = esp_slave_destroy,
+ .eh_abort_handler = esp_eh_abort_handler,
+ .eh_bus_reset_handler = esp_eh_bus_reset_handler,
+ .eh_host_reset_handler = esp_eh_host_reset_handler,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .use_clustering = ENABLE_CLUSTERING,
+ .max_sectors = 0xffff,
+ .skip_settle_delay = 1,
+};
+EXPORT_SYMBOL(scsi_esp_template);
+
+static void esp_get_signalling(struct Scsi_Host *host)
+{
+ struct esp *esp = host_to_esp(host);
+ enum spi_signal_type type;
+
+ if (esp->flags & ESP_FLAG_DIFFERENTIAL)
+ type = SPI_SIGNAL_HVD;
+ else
+ type = SPI_SIGNAL_SE;
+
+ spi_signalling(host) = type;
+}
+
+static void esp_set_offset(struct scsi_target *target, int offset)
+{
+ struct Scsi_Host *host = dev_to_shost(target->dev.parent);
+ struct esp *esp = host_to_esp(host);
+ struct esp_target_data *tp = &esp->target[target->id];
+
+ tp->nego_goal_offset = offset;
+ tp->flags |= ESP_TGT_CHECK_NEGO;
+}
+
+static void esp_set_period(struct scsi_target *target, int period)
+{
+ struct Scsi_Host *host = dev_to_shost(target->dev.parent);
+ struct esp *esp = host_to_esp(host);
+ struct esp_target_data *tp = &esp->target[target->id];
+
+ tp->nego_goal_period = period;
+ tp->flags |= ESP_TGT_CHECK_NEGO;
+}
+
+static void esp_set_width(struct scsi_target *target, int width)
+{
+ struct Scsi_Host *host = dev_to_shost(target->dev.parent);
+ struct esp *esp = host_to_esp(host);
+ struct esp_target_data *tp = &esp->target[target->id];
+
+ tp->nego_goal_width = (width ? 1 : 0);
+ tp->flags |= ESP_TGT_CHECK_NEGO;
+}
+
+static struct spi_function_template esp_transport_ops = {
+ .set_offset = esp_set_offset,
+ .show_offset = 1,
+ .set_period = esp_set_period,
+ .show_period = 1,
+ .set_width = esp_set_width,
+ .show_width = 1,
+ .get_signalling = esp_get_signalling,
+};
+
+static int __init esp_init(void)
+{
+ BUILD_BUG_ON(sizeof(struct scsi_pointer) <
+ sizeof(struct esp_cmd_priv));
+
+ esp_transport_template = spi_attach_transport(&esp_transport_ops);
+ if (!esp_transport_template)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void __exit esp_exit(void)
+{
+ spi_release_transport(esp_transport_template);
+}
+
+MODULE_DESCRIPTION("ESP SCSI driver core");
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_param(esp_bus_reset_settle, int, 0);
+MODULE_PARM_DESC(esp_bus_reset_settle,
+ "ESP scsi bus reset delay in seconds");
+
+module_param(esp_debug, int, 0);
+MODULE_PARM_DESC(esp_debug,
+"ESP bitmapped debugging message enable value:\n"
+" 0x00000001 Log interrupt events\n"
+" 0x00000002 Log scsi commands\n"
+" 0x00000004 Log resets\n"
+" 0x00000008 Log message in events\n"
+" 0x00000010 Log message out events\n"
+" 0x00000020 Log command completion\n"
+" 0x00000040 Log disconnects\n"
+" 0x00000080 Log data start\n"
+" 0x00000100 Log data done\n"
+" 0x00000200 Log reconnects\n"
+" 0x00000400 Log auto-sense data\n"
+);
+
+module_init(esp_init);
+module_exit(esp_exit);
diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h
new file mode 100644
index 000000000000..8d4a6690401f
--- /dev/null
+++ b/drivers/scsi/esp_scsi.h
@@ -0,0 +1,560 @@
+/* esp_scsi.h: Defines and structures for the ESP drier.
+ *
+ * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#ifndef _ESP_SCSI_H
+#define _ESP_SCSI_H
+
+ /* Access Description Offset */
+#define ESP_TCLOW 0x00UL /* rw Low bits transfer count 0x00 */
+#define ESP_TCMED 0x01UL /* rw Mid bits transfer count 0x04 */
+#define ESP_FDATA 0x02UL /* rw FIFO data bits 0x08 */
+#define ESP_CMD 0x03UL /* rw SCSI command bits 0x0c */
+#define ESP_STATUS 0x04UL /* ro ESP status register 0x10 */
+#define ESP_BUSID ESP_STATUS /* wo BusID for sel/resel 0x10 */
+#define ESP_INTRPT 0x05UL /* ro Kind of interrupt 0x14 */
+#define ESP_TIMEO ESP_INTRPT /* wo Timeout for sel/resel 0x14 */
+#define ESP_SSTEP 0x06UL /* ro Sequence step register 0x18 */
+#define ESP_STP ESP_SSTEP /* wo Transfer period/sync 0x18 */
+#define ESP_FFLAGS 0x07UL /* ro Bits current FIFO info 0x1c */
+#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */
+#define ESP_CFG1 0x08UL /* rw First cfg register 0x20 */
+#define ESP_CFACT 0x09UL /* wo Clock conv factor 0x24 */
+#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */
+#define ESP_CTEST 0x0aUL /* wo Chip test register 0x28 */
+#define ESP_CFG2 0x0bUL /* rw Second cfg register 0x2c */
+#define ESP_CFG3 0x0cUL /* rw Third cfg register 0x30 */
+#define ESP_TCHI 0x0eUL /* rw High bits transf count 0x38 */
+#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */
+#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */
+#define ESP_FGRND 0x0fUL /* rw Data base for fifo 0x3c */
+#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */
+
+#define SBUS_ESP_REG_SIZE 0x40UL
+
+/* Bitfield meanings for the above registers. */
+
+/* ESP config reg 1, read-write, found on all ESP chips */
+#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */
+#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */
+#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */
+#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */
+#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */
+#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */
+
+/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */
+#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */
+#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */
+#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */
+#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tgtmode) */
+#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */
+#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */
+#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */
+#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */
+#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,216) */
+#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (236) */
+#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */
+#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */
+#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */
+
+/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */
+#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */
+#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */
+#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */
+#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */
+#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */
+#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */
+#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */
+#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */
+#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */
+#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */
+#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */
+#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */
+#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */
+#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */
+#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */
+#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */
+
+/* ESP command register read-write */
+/* Group 1 commands: These may be sent at any point in time to the ESP
+ * chip. None of them can generate interrupts 'cept
+ * the "SCSI bus reset" command if you have not disabled
+ * SCSI reset interrupts in the config1 ESP register.
+ */
+#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */
+#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */
+#define ESP_CMD_RC 0x02 /* Chip reset */
+#define ESP_CMD_RS 0x03 /* SCSI bus reset */
+
+/* Group 2 commands: ESP must be an initiator and connected to a target
+ * for these commands to work.
+ */
+#define ESP_CMD_TI 0x10 /* Transfer Information */
+#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */
+#define ESP_CMD_MOK 0x12 /* Message okie-dokie */
+#define ESP_CMD_TPAD 0x18 /* Transfer Pad */
+#define ESP_CMD_SATN 0x1a /* Set ATN */
+#define ESP_CMD_RATN 0x1b /* De-assert ATN */
+
+/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected
+ * to a target as the initiator for these commands to work.
+ */
+#define ESP_CMD_SMSG 0x20 /* Send message */
+#define ESP_CMD_SSTAT 0x21 /* Send status */
+#define ESP_CMD_SDATA 0x22 /* Send data */
+#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */
+#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */
+#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */
+#define ESP_CMD_DCNCT 0x27 /* Disconnect */
+#define ESP_CMD_RMSG 0x28 /* Receive Message */
+#define ESP_CMD_RCMD 0x29 /* Receive Command */
+#define ESP_CMD_RDATA 0x2a /* Receive Data */
+#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */
+
+/* Group 4 commands: The ESP must be in the disconnected state and must
+ * not be connected to any targets as initiator for
+ * these commands to work.
+ */
+#define ESP_CMD_RSEL 0x40 /* Reselect */
+#define ESP_CMD_SEL 0x41 /* Select w/o ATN */
+#define ESP_CMD_SELA 0x42 /* Select w/ATN */
+#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */
+#define ESP_CMD_ESEL 0x44 /* Enable selection */
+#define ESP_CMD_DSEL 0x45 /* Disable selections */
+#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */
+#define ESP_CMD_RSEL3 0x47 /* Reselect3 */
+
+/* This bit enables the ESP's DMA on the SBus */
+#define ESP_CMD_DMA 0x80 /* Do DMA? */
+
+/* ESP status register read-only */
+#define ESP_STAT_PIO 0x01 /* IO phase bit */
+#define ESP_STAT_PCD 0x02 /* CD phase bit */
+#define ESP_STAT_PMSG 0x04 /* MSG phase bit */
+#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */
+#define ESP_STAT_TDONE 0x08 /* Transfer Completed */
+#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */
+#define ESP_STAT_PERR 0x20 /* Parity error */
+#define ESP_STAT_SPAM 0x40 /* Real bad error */
+/* This indicates the 'interrupt pending' condition on esp236, it is a reserved
+ * bit on other revs of the ESP.
+ */
+#define ESP_STAT_INTR 0x80 /* Interrupt */
+
+/* The status register can be masked with ESP_STAT_PMASK and compared
+ * with the following values to determine the current phase the ESP
+ * (at least thinks it) is in. For our purposes we also add our own
+ * software 'done' bit for our phase management engine.
+ */
+#define ESP_DOP (0) /* Data Out */
+#define ESP_DIP (ESP_STAT_PIO) /* Data In */
+#define ESP_CMDP (ESP_STAT_PCD) /* Command */
+#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */
+#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */
+#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */
+
+/* HME only: status 2 register */
+#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */
+#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */
+#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */
+#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */
+#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */
+#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */
+#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */
+#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */
+
+/* ESP interrupt register read-only */
+#define ESP_INTR_S 0x01 /* Select w/o ATN */
+#define ESP_INTR_SATN 0x02 /* Select w/ATN */
+#define ESP_INTR_RSEL 0x04 /* Reselected */
+#define ESP_INTR_FDONE 0x08 /* Function done */
+#define ESP_INTR_BSERV 0x10 /* Bus service */
+#define ESP_INTR_DC 0x20 /* Disconnect */
+#define ESP_INTR_IC 0x40 /* Illegal command given */
+#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */
+
+/* ESP sequence step register read-only */
+#define ESP_STEP_VBITS 0x07 /* Valid bits */
+#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */
+#define ESP_STEP_SID 0x01 /* One msg byte sent */
+#define ESP_STEP_NCMD 0x02 /* Was not in command phase */
+#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd
+ * bytes to be lost
+ */
+#define ESP_STEP_FINI4 0x04 /* Command was sent ok */
+
+/* Ho hum, some ESP's set the step register to this as well... */
+#define ESP_STEP_FINI5 0x05
+#define ESP_STEP_FINI6 0x06
+#define ESP_STEP_FINI7 0x07
+
+/* ESP chip-test register read-write */
+#define ESP_TEST_TARG 0x01 /* Target test mode */
+#define ESP_TEST_INI 0x02 /* Initiator test mode */
+#define ESP_TEST_TS 0x04 /* Tristate test mode */
+
+/* ESP unique ID register read-only, found on fas236+fas100a only */
+#define ESP_UID_F100A 0x00 /* ESP FAS100A */
+#define ESP_UID_F236 0x02 /* ESP FAS236 */
+#define ESP_UID_REV 0x07 /* ESP revision */
+#define ESP_UID_FAM 0xf8 /* ESP family */
+
+/* ESP fifo flags register read-only */
+/* Note that the following implies a 16 byte FIFO on the ESP. */
+#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */
+#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */
+#define ESP_FF_SSTEP 0xe0 /* Sequence step */
+
+/* ESP clock conversion factor register write-only */
+#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */
+#define ESP_CCF_NEVER 0x01 /* Set it to this and die */
+#define ESP_CCF_F2 0x02 /* 10MHz */
+#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */
+#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */
+#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */
+#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */
+#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */
+
+/* HME only... */
+#define ESP_BUSID_RESELID 0x10
+#define ESP_BUSID_CTR32BIT 0x40
+
+#define ESP_BUS_TIMEOUT 250 /* In milli-seconds */
+#define ESP_TIMEO_CONST 8192
+#define ESP_NEG_DEFP(mhz, cfact) \
+ ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
+#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000))
+#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000))
+
+/* For slow to medium speed input clock rates we shoot for 5mb/s, but for high
+ * input clock rates we try to do 10mb/s although I don't think a transfer can
+ * even run that fast with an ESP even with DMA2 scatter gather pipelining.
+ */
+#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */
+#define SYNC_DEFP_FAST 0x19 /* 10mb/s */
+
+struct esp_cmd_priv {
+ union {
+ dma_addr_t dma_addr;
+ int num_sg;
+ } u;
+
+ unsigned int cur_residue;
+ struct scatterlist *cur_sg;
+ unsigned int tot_residue;
+};
+#define ESP_CMD_PRIV(CMD) ((struct esp_cmd_priv *)(&(CMD)->SCp))
+
+enum esp_rev {
+ ESP100 = 0x00, /* NCR53C90 - very broken */
+ ESP100A = 0x01, /* NCR53C90A */
+ ESP236 = 0x02,
+ FAS236 = 0x03,
+ FAS100A = 0x04,
+ FAST = 0x05,
+ FASHME = 0x06,
+};
+
+struct esp_cmd_entry {
+ struct list_head list;
+
+ struct scsi_cmnd *cmd;
+
+ unsigned int saved_cur_residue;
+ struct scatterlist *saved_cur_sg;
+ unsigned int saved_tot_residue;
+
+ u8 flags;
+#define ESP_CMD_FLAG_WRITE 0x01 /* DMA is a write */
+#define ESP_CMD_FLAG_ABORT 0x02 /* being aborted */
+#define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */
+
+ u8 tag[2];
+
+ u8 status;
+ u8 message;
+
+ unsigned char *sense_ptr;
+ unsigned char *saved_sense_ptr;
+ dma_addr_t sense_dma;
+
+ struct completion *eh_done;
+};
+
+/* XXX make this configurable somehow XXX */
+#define ESP_DEFAULT_TAGS 16
+
+#define ESP_MAX_TARGET 16
+#define ESP_MAX_LUN 8
+#define ESP_MAX_TAG 256
+
+struct esp_lun_data {
+ struct esp_cmd_entry *non_tagged_cmd;
+ int num_tagged;
+ int hold;
+ struct esp_cmd_entry *tagged_cmds[ESP_MAX_TAG];
+};
+
+struct esp_target_data {
+ /* These are the ESP_STP, ESP_SOFF, and ESP_CFG3 register values which
+ * match the currently negotiated settings for this target. The SCSI
+ * protocol values are maintained in spi_{offset,period,wide}(starget).
+ */
+ u8 esp_period;
+ u8 esp_offset;
+ u8 esp_config3;
+
+ u8 flags;
+#define ESP_TGT_WIDE 0x01
+#define ESP_TGT_DISCONNECT 0x02
+#define ESP_TGT_NEGO_WIDE 0x04
+#define ESP_TGT_NEGO_SYNC 0x08
+#define ESP_TGT_CHECK_NEGO 0x40
+#define ESP_TGT_BROKEN 0x80
+
+ /* When ESP_TGT_CHECK_NEGO is set, on the next scsi command to this
+ * device we will try to negotiate the following parameters.
+ */
+ u8 nego_goal_period;
+ u8 nego_goal_offset;
+ u8 nego_goal_width;
+ u8 nego_goal_tags;
+
+ struct scsi_target *starget;
+};
+
+struct esp_event_ent {
+ u8 type;
+#define ESP_EVENT_TYPE_EVENT 0x01
+#define ESP_EVENT_TYPE_CMD 0x02
+ u8 val;
+
+ u8 sreg;
+ u8 seqreg;
+ u8 sreg2;
+ u8 ireg;
+ u8 select_state;
+ u8 event;
+ u8 __pad;
+};
+
+struct esp;
+struct esp_driver_ops {
+ /* Read and write the ESP 8-bit registers. On some
+ * applications of the ESP chip the registers are at 4-byte
+ * instead of 1-byte intervals.
+ */
+ void (*esp_write8)(struct esp *esp, u8 val, unsigned long reg);
+ u8 (*esp_read8)(struct esp *esp, unsigned long reg);
+
+ /* Map and unmap DMA memory. Eventually the driver will be
+ * converted to the generic DMA API as soon as SBUS is able to
+ * cope with that. At such time we can remove this.
+ */
+ dma_addr_t (*map_single)(struct esp *esp, void *buf,
+ size_t sz, int dir);
+ int (*map_sg)(struct esp *esp, struct scatterlist *sg,
+ int num_sg, int dir);
+ void (*unmap_single)(struct esp *esp, dma_addr_t addr,
+ size_t sz, int dir);
+ void (*unmap_sg)(struct esp *esp, struct scatterlist *sg,
+ int num_sg, int dir);
+
+ /* Return non-zero if there is an IRQ pending. Usually this
+ * status bit lives in the DMA controller sitting in front of
+ * the ESP. This has to be accurate or else the ESP interrupt
+ * handler will not run.
+ */
+ int (*irq_pending)(struct esp *esp);
+
+ /* Reset the DMA engine entirely. On return, ESP interrupts
+ * should be enabled. Often the interrupt enabling is
+ * controlled in the DMA engine.
+ */
+ void (*reset_dma)(struct esp *esp);
+
+ /* Drain any pending DMA in the DMA engine after a transfer.
+ * This is for writes to memory.
+ */
+ void (*dma_drain)(struct esp *esp);
+
+ /* Invalidate the DMA engine after a DMA transfer. */
+ void (*dma_invalidate)(struct esp *esp);
+
+ /* Setup an ESP command that will use a DMA transfer.
+ * The 'esp_count' specifies what transfer length should be
+ * programmed into the ESP transfer counter registers, whereas
+ * the 'dma_count' is the length that should be programmed into
+ * the DMA controller. Usually they are the same. If 'write'
+ * is non-zero, this transfer is a write into memory. 'cmd'
+ * holds the ESP command that should be issued by calling
+ * scsi_esp_cmd() at the appropriate time while programming
+ * the DMA hardware.
+ */
+ void (*send_dma_cmd)(struct esp *esp, u32 dma_addr, u32 esp_count,
+ u32 dma_count, int write, u8 cmd);
+
+ /* Return non-zero if the DMA engine is reporting an error
+ * currently.
+ */
+ int (*dma_error)(struct esp *esp);
+};
+
+#define ESP_MAX_MSG_SZ 8
+#define ESP_EVENT_LOG_SZ 32
+
+#define ESP_QUICKIRQ_LIMIT 100
+#define ESP_RESELECT_TAG_LIMIT 2500
+
+struct esp {
+ void __iomem *regs;
+ void __iomem *dma_regs;
+
+ const struct esp_driver_ops *ops;
+
+ struct Scsi_Host *host;
+ void *dev;
+
+ struct esp_cmd_entry *active_cmd;
+
+ struct list_head queued_cmds;
+ struct list_head active_cmds;
+
+ u8 *command_block;
+ dma_addr_t command_block_dma;
+
+ unsigned int data_dma_len;
+
+ /* The following are used to determine the cause of an IRQ. Upon every
+ * IRQ entry we synchronize these with the hardware registers.
+ */
+ u8 sreg;
+ u8 seqreg;
+ u8 sreg2;
+ u8 ireg;
+
+ u32 prev_hme_dmacsr;
+ u8 prev_soff;
+ u8 prev_stp;
+ u8 prev_cfg3;
+ u8 __pad;
+
+ struct list_head esp_cmd_pool;
+
+ struct esp_target_data target[ESP_MAX_TARGET];
+
+ int fifo_cnt;
+ u8 fifo[16];
+
+ struct esp_event_ent esp_event_log[ESP_EVENT_LOG_SZ];
+ int esp_event_cur;
+
+ u8 msg_out[ESP_MAX_MSG_SZ];
+ int msg_out_len;
+
+ u8 msg_in[ESP_MAX_MSG_SZ];
+ int msg_in_len;
+
+ u8 bursts;
+ u8 config1;
+ u8 config2;
+
+ u8 scsi_id;
+ u32 scsi_id_mask;
+
+ enum esp_rev rev;
+
+ u32 flags;
+#define ESP_FLAG_DIFFERENTIAL 0x00000001
+#define ESP_FLAG_RESETTING 0x00000002
+#define ESP_FLAG_DOING_SLOWCMD 0x00000004
+#define ESP_FLAG_WIDE_CAPABLE 0x00000008
+#define ESP_FLAG_QUICKIRQ_CHECK 0x00000010
+
+ u8 select_state;
+#define ESP_SELECT_NONE 0x00 /* Not selecting */
+#define ESP_SELECT_BASIC 0x01 /* Select w/o MSGOUT phase */
+#define ESP_SELECT_MSGOUT 0x02 /* Select with MSGOUT */
+
+ /* When we are not selecting, we are expecting an event. */
+ u8 event;
+#define ESP_EVENT_NONE 0x00
+#define ESP_EVENT_CMD_START 0x01
+#define ESP_EVENT_CMD_DONE 0x02
+#define ESP_EVENT_DATA_IN 0x03
+#define ESP_EVENT_DATA_OUT 0x04
+#define ESP_EVENT_DATA_DONE 0x05
+#define ESP_EVENT_MSGIN 0x06
+#define ESP_EVENT_MSGIN_MORE 0x07
+#define ESP_EVENT_MSGIN_DONE 0x08
+#define ESP_EVENT_MSGOUT 0x09
+#define ESP_EVENT_MSGOUT_DONE 0x0a
+#define ESP_EVENT_STATUS 0x0b
+#define ESP_EVENT_FREE_BUS 0x0c
+#define ESP_EVENT_CHECK_PHASE 0x0d
+#define ESP_EVENT_RESET 0x10
+
+ /* Probed in esp_get_clock_params() */
+ u32 cfact;
+ u32 cfreq;
+ u32 ccycle;
+ u32 ctick;
+ u32 neg_defp;
+ u32 sync_defp;
+
+ /* Computed in esp_reset_esp() */
+ u32 max_period;
+ u32 min_period;
+ u32 radelay;
+
+ /* Slow command state. */
+ u8 *cmd_bytes_ptr;
+ int cmd_bytes_left;
+
+ struct completion *eh_reset;
+
+ struct sbus_dma *dma;
+};
+
+#define host_to_esp(host) ((struct esp *)(host)->hostdata)
+
+/* A front-end driver for the ESP chip should do the following in
+ * it's device probe routine:
+ * 1) Allocate the host and private area using scsi_host_alloc()
+ * with size 'sizeof(struct esp)'. The first argument to
+ * scsi_host_alloc() should be &scsi_esp_template.
+ * 2) Set host->max_id as appropriate.
+ * 3) Set esp->host to the scsi_host itself, and esp->dev
+ * to the device object pointer.
+ * 4) Hook up esp->ops to the front-end implementation.
+ * 5) If the ESP chip supports wide transfers, set ESP_FLAG_WIDE_CAPABLE
+ * in esp->flags.
+ * 6) Map the DMA and ESP chip registers.
+ * 7) DMA map the ESP command block, store the DMA address
+ * in esp->command_block_dma.
+ * 8) Register the scsi_esp_intr() interrupt handler.
+ * 9) Probe for and provide the following chip properties:
+ * esp->scsi_id (assign to esp->host->this_id too)
+ * esp->scsi_id_mask
+ * If ESP bus is differential, set ESP_FLAG_DIFFERENTIAL
+ * esp->cfreq
+ * DMA burst bit mask in esp->bursts, if necessary
+ * 10) Perform any actions necessary before the ESP device can
+ * be programmed for the first time. On some configs, for
+ * example, the DMA engine has to be reset before ESP can
+ * be programmed.
+ * 11) If necessary, call dev_set_drvdata() as needed.
+ * 12) Call scsi_esp_register() with prepared 'esp' structure
+ * and a device pointer if possible.
+ * 13) Check scsi_esp_register() return value, release all resources
+ * if an error was returned.
+ */
+extern struct scsi_host_template scsi_esp_template;
+extern int scsi_esp_register(struct esp *, struct device *);
+
+extern void scsi_esp_unregister(struct esp *);
+extern irqreturn_t scsi_esp_intr(int, void *);
+extern void scsi_esp_cmd(struct esp *, u8);
+
+#endif /* !(_ESP_SCSI_H) */
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 38c3a291efac..bd8e7f323c69 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -435,7 +435,7 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
struct class_device *cdev;
struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p;
- down_read(&class->subsys.rwsem);
+ down(&class->sem);
list_for_each_entry(cdev, &class->children, node) {
p = class_to_shost(cdev);
if (p->host_no == hostnum) {
@@ -443,7 +443,7 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
break;
}
}
- up_read(&class->subsys.rwsem);
+ up(&class->sem);
return shost;
}
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index 9b827ceec501..c4195ea869e9 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -1281,7 +1281,7 @@ static struct scsi_cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti)
(struct scatterlist *)Cmnd->request_buffer,
Cmnd->use_sg,
Cmnd->sc_data_direction);
- } else {
+ } else if (Cmnd->request_bufflen) {
sbus_unmap_single(qpti->sdev,
(__u32)((unsigned long)Cmnd->SCp.ptr),
Cmnd->request_bufflen,
@@ -1403,7 +1403,7 @@ static int __devinit qpti_sbus_probe(struct of_device *dev, const struct of_devi
struct scsi_host_template *tpnt = match->data;
struct Scsi_Host *host;
struct qlogicpti *qpti;
- char *fcode;
+ const char *fcode;
/* Sometimes Antares cards come up not completely
* setup, and we get a report of a zero IRQ.
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c
index 1b59b27e887f..4bf9aa547c78 100644
--- a/drivers/scsi/scsi_netlink.c
+++ b/drivers/scsi/scsi_netlink.c
@@ -50,7 +50,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
while (skb->len >= NLMSG_SPACE(0)) {
err = 0;
- nlh = (struct nlmsghdr *) skb->data;
+ nlh = nlmsg_hdr(skb);
if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
(skb->len < nlh->nlmsg_len)) {
printk(KERN_WARNING "%s: discarding partial skb\n",
@@ -168,7 +168,8 @@ scsi_netlink_init(void)
}
scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT,
- SCSI_NL_GRP_CNT, scsi_nl_rcv, THIS_MODULE);
+ SCSI_NL_GRP_CNT, scsi_nl_rcv, NULL,
+ THIS_MODULE);
if (!scsi_nl_sock) {
printk(KERN_ERR "%s: register of recieve handler failed\n",
__FUNCTION__);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index ce0d14af33c8..aabaa0576ab4 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -1081,7 +1081,7 @@ iscsi_if_rx(struct sock *sk, int len)
struct nlmsghdr *nlh;
struct iscsi_uevent *ev;
- nlh = (struct nlmsghdr *)skb->data;
+ nlh = nlmsg_hdr(skb);
if (nlh->nlmsg_len < sizeof(*nlh) ||
skb->len < nlh->nlmsg_len) {
break;
@@ -1435,7 +1435,7 @@ static __init int iscsi_transport_init(void)
if (err)
goto unregister_conn_class;
- nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx,
+ nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
THIS_MODULE);
if (!nls) {
err = -ENOBUFS;
diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c
new file mode 100644
index 000000000000..8c766bcd1095
--- /dev/null
+++ b/drivers/scsi/sun_esp.c
@@ -0,0 +1,634 @@
+/* sun_esp.c: ESP front-end for Sparc SBUS systems.
+ *
+ * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <asm/sbus.h>
+
+#include <scsi/scsi_host.h>
+
+#include "esp_scsi.h"
+
+#define DRV_MODULE_NAME "sun_esp"
+#define PFX DRV_MODULE_NAME ": "
+#define DRV_VERSION "1.000"
+#define DRV_MODULE_RELDATE "April 19, 2007"
+
+#define dma_read32(REG) \
+ sbus_readl(esp->dma_regs + (REG))
+#define dma_write32(VAL, REG) \
+ sbus_writel((VAL), esp->dma_regs + (REG))
+
+static int __devinit esp_sbus_find_dma(struct esp *esp, struct sbus_dev *dma_sdev)
+{
+ struct sbus_dev *sdev = esp->dev;
+ struct sbus_dma *dma;
+
+ if (dma_sdev != NULL) {
+ for_each_dvma(dma) {
+ if (dma->sdev == dma_sdev)
+ break;
+ }
+ } else {
+ for_each_dvma(dma) {
+ if (dma->sdev == NULL)
+ break;
+
+ /* If bus + slot are the same and it has the
+ * correct OBP name, it's ours.
+ */
+ if (sdev->bus == dma->sdev->bus &&
+ sdev->slot == dma->sdev->slot &&
+ (!strcmp(dma->sdev->prom_name, "dma") ||
+ !strcmp(dma->sdev->prom_name, "espdma")))
+ break;
+ }
+ }
+
+ if (dma == NULL) {
+ printk(KERN_ERR PFX "[%s] Cannot find dma.\n",
+ sdev->ofdev.node->full_name);
+ return -ENODEV;
+ }
+ esp->dma = dma;
+ esp->dma_regs = dma->regs;
+
+ return 0;
+
+}
+
+static int __devinit esp_sbus_map_regs(struct esp *esp, int hme)
+{
+ struct sbus_dev *sdev = esp->dev;
+ struct resource *res;
+
+ /* On HME, two reg sets exist, first is DVMA,
+ * second is ESP registers.
+ */
+ if (hme)
+ res = &sdev->resource[1];
+ else
+ res = &sdev->resource[0];
+
+ esp->regs = sbus_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP");
+ if (!esp->regs)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int __devinit esp_sbus_map_command_block(struct esp *esp)
+{
+ struct sbus_dev *sdev = esp->dev;
+
+ esp->command_block = sbus_alloc_consistent(sdev, 16,
+ &esp->command_block_dma);
+ if (!esp->command_block)
+ return -ENOMEM;
+ return 0;
+}
+
+static int __devinit esp_sbus_register_irq(struct esp *esp)
+{
+ struct Scsi_Host *host = esp->host;
+ struct sbus_dev *sdev = esp->dev;
+
+ host->irq = sdev->irqs[0];
+ return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp);
+}
+
+static void __devinit esp_get_scsi_id(struct esp *esp)
+{
+ struct sbus_dev *sdev = esp->dev;
+ struct device_node *dp = sdev->ofdev.node;
+
+ esp->scsi_id = of_getintprop_default(dp, "initiator-id", 0xff);
+ if (esp->scsi_id != 0xff)
+ goto done;
+
+ esp->scsi_id = of_getintprop_default(dp, "scsi-initiator-id", 0xff);
+ if (esp->scsi_id != 0xff)
+ goto done;
+
+ if (!sdev->bus) {
+ /* SUN4 */
+ esp->scsi_id = 7;
+ goto done;
+ }
+
+ esp->scsi_id = of_getintprop_default(sdev->bus->ofdev.node,
+ "scsi-initiator-id", 7);
+
+done:
+ esp->host->this_id = esp->scsi_id;
+ esp->scsi_id_mask = (1 << esp->scsi_id);
+}
+
+static void __devinit esp_get_differential(struct esp *esp)
+{
+ struct sbus_dev *sdev = esp->dev;
+ struct device_node *dp = sdev->ofdev.node;
+
+ if (of_find_property(dp, "differential", NULL))
+ esp->flags |= ESP_FLAG_DIFFERENTIAL;
+ else
+ esp->flags &= ~ESP_FLAG_DIFFERENTIAL;
+}
+
+static void __devinit esp_get_clock_params(struct esp *esp)
+{
+ struct sbus_dev *sdev = esp->dev;
+ struct device_node *dp = sdev->ofdev.node;
+ struct device_node *bus_dp;
+ int fmhz;
+
+ bus_dp = NULL;
+ if (sdev != NULL && sdev->bus != NULL)
+ bus_dp = sdev->bus->ofdev.node;
+
+ fmhz = of_getintprop_default(dp, "clock-frequency", 0);
+ if (fmhz == 0)
+ fmhz = (!bus_dp) ? 0 :
+ of_getintprop_default(bus_dp, "clock-frequency", 0);
+
+ esp->cfreq = fmhz;
+}
+
+static void __devinit esp_get_bursts(struct esp *esp, struct sbus_dev *dma)
+{
+ struct sbus_dev *sdev = esp->dev;
+ struct device_node *dp = sdev->ofdev.node;
+ u8 bursts;
+
+ bursts = of_getintprop_default(dp, "burst-sizes", 0xff);
+ if (dma) {
+ struct device_node *dma_dp = dma->ofdev.node;
+ u8 val = of_getintprop_default(dma_dp, "burst-sizes", 0xff);
+ if (val != 0xff)
+ bursts &= val;
+ }
+
+ if (sdev->bus) {
+ u8 val = of_getintprop_default(sdev->bus->ofdev.node,
+ "burst-sizes", 0xff);
+ if (val != 0xff)
+ bursts &= val;
+ }
+
+ if (bursts == 0xff ||
+ (bursts & DMA_BURST16) == 0 ||
+ (bursts & DMA_BURST32) == 0)
+ bursts = (DMA_BURST32 - 1);
+
+ esp->bursts = bursts;
+}
+
+static void __devinit esp_sbus_get_props(struct esp *esp, struct sbus_dev *espdma)
+{
+ esp_get_scsi_id(esp);
+ esp_get_differential(esp);
+ esp_get_clock_params(esp);
+ esp_get_bursts(esp, espdma);
+}
+
+static void sbus_esp_write8(struct esp *esp, u8 val, unsigned long reg)
+{
+ sbus_writeb(val, esp->regs + (reg * 4UL));
+}
+
+static u8 sbus_esp_read8(struct esp *esp, unsigned long reg)
+{
+ return sbus_readb(esp->regs + (reg * 4UL));
+}
+
+static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf,
+ size_t sz, int dir)
+{
+ return sbus_map_single(esp->dev, buf, sz, dir);
+}
+
+static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg,
+ int num_sg, int dir)
+{
+ return sbus_map_sg(esp->dev, sg, num_sg, dir);
+}
+
+static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr,
+ size_t sz, int dir)
+{
+ sbus_unmap_single(esp->dev, addr, sz, dir);
+}
+
+static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
+ int num_sg, int dir)
+{
+ sbus_unmap_sg(esp->dev, sg, num_sg, dir);
+}
+
+static int sbus_esp_irq_pending(struct esp *esp)
+{
+ if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))
+ return 1;
+ return 0;
+}
+
+static void sbus_esp_reset_dma(struct esp *esp)
+{
+ int can_do_burst16, can_do_burst32, can_do_burst64;
+ int can_do_sbus64, lim;
+ u32 val;
+
+ can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
+ can_do_burst32 = (esp->bursts & DMA_BURST32) != 0;
+ can_do_burst64 = 0;
+ can_do_sbus64 = 0;
+ if (sbus_can_dma_64bit(esp->dev))
+ can_do_sbus64 = 1;
+ if (sbus_can_burst64(esp->sdev))
+ can_do_burst64 = (esp->bursts & DMA_BURST64) != 0;
+
+ /* Put the DVMA into a known state. */
+ if (esp->dma->revision != dvmahme) {
+ val = dma_read32(DMA_CSR);
+ dma_write32(val | DMA_RST_SCSI, DMA_CSR);
+ dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
+ }
+ switch (esp->dma->revision) {
+ case dvmahme:
+ dma_write32(DMA_RESET_FAS366, DMA_CSR);
+ dma_write32(DMA_RST_SCSI, DMA_CSR);
+
+ esp->prev_hme_dmacsr = (DMA_PARITY_OFF | DMA_2CLKS |
+ DMA_SCSI_DISAB | DMA_INT_ENAB);
+
+ esp->prev_hme_dmacsr &= ~(DMA_ENABLE | DMA_ST_WRITE |
+ DMA_BRST_SZ);
+
+ if (can_do_burst64)
+ esp->prev_hme_dmacsr |= DMA_BRST64;
+ else if (can_do_burst32)
+ esp->prev_hme_dmacsr |= DMA_BRST32;
+
+ if (can_do_sbus64) {
+ esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64;
+ sbus_set_sbus64(esp->dev, esp->bursts);
+ }
+
+ lim = 1000;
+ while (dma_read32(DMA_CSR) & DMA_PEND_READ) {
+ if (--lim == 0) {
+ printk(KERN_ALERT PFX "esp%d: DMA_PEND_READ "
+ "will not clear!\n",
+ esp->host->unique_id);
+ break;
+ }
+ udelay(1);
+ }
+
+ dma_write32(0, DMA_CSR);
+ dma_write32(esp->prev_hme_dmacsr, DMA_CSR);
+
+ dma_write32(0, DMA_ADDR);
+ break;
+
+ case dvmarev2:
+ if (esp->rev != ESP100) {
+ val = dma_read32(DMA_CSR);
+ dma_write32(val | DMA_3CLKS, DMA_CSR);
+ }
+ break;
+
+ case dvmarev3:
+ val = dma_read32(DMA_CSR);
+ val &= ~DMA_3CLKS;
+ val |= DMA_2CLKS;
+ if (can_do_burst32) {
+ val &= ~DMA_BRST_SZ;
+ val |= DMA_BRST32;
+ }
+ dma_write32(val, DMA_CSR);
+ break;
+
+ case dvmaesc1:
+ val = dma_read32(DMA_CSR);
+ val |= DMA_ADD_ENABLE;
+ val &= ~DMA_BCNT_ENAB;
+ if (!can_do_burst32 && can_do_burst16) {
+ val |= DMA_ESC_BURST;
+ } else {
+ val &= ~(DMA_ESC_BURST);
+ }
+ dma_write32(val, DMA_CSR);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Enable interrupts. */
+ val = dma_read32(DMA_CSR);
+ dma_write32(val | DMA_INT_ENAB, DMA_CSR);
+}
+
+static void sbus_esp_dma_drain(struct esp *esp)
+{
+ u32 csr;
+ int lim;
+
+ if (esp->dma->revision == dvmahme)
+ return;
+
+ csr = dma_read32(DMA_CSR);
+ if (!(csr & DMA_FIFO_ISDRAIN))
+ return;
+
+ if (esp->dma->revision != dvmarev3 && esp->dma->revision != dvmaesc1)
+ dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR);
+
+ lim = 1000;
+ while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) {
+ if (--lim == 0) {
+ printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n",
+ esp->host->unique_id);
+ break;
+ }
+ udelay(1);
+ }
+}
+
+static void sbus_esp_dma_invalidate(struct esp *esp)
+{
+ if (esp->dma->revision == dvmahme) {
+ dma_write32(DMA_RST_SCSI, DMA_CSR);
+
+ esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
+ (DMA_PARITY_OFF | DMA_2CLKS |
+ DMA_SCSI_DISAB | DMA_INT_ENAB)) &
+ ~(DMA_ST_WRITE | DMA_ENABLE));
+
+ dma_write32(0, DMA_CSR);
+ dma_write32(esp->prev_hme_dmacsr, DMA_CSR);
+
+ /* This is necessary to avoid having the SCSI channel
+ * engine lock up on us.
+ */
+ dma_write32(0, DMA_ADDR);
+ } else {
+ u32 val;
+ int lim;
+
+ lim = 1000;
+ while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) {
+ if (--lim == 0) {
+ printk(KERN_ALERT PFX "esp%d: DMA will not "
+ "invalidate!\n", esp->host->unique_id);
+ break;
+ }
+ udelay(1);
+ }
+
+ val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
+ val |= DMA_FIFO_INV;
+ dma_write32(val, DMA_CSR);
+ val &= ~DMA_FIFO_INV;
+ dma_write32(val, DMA_CSR);
+ }
+}
+
+static void sbus_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
+ u32 dma_count, int write, u8 cmd)
+{
+ u32 csr;
+
+ BUG_ON(!(cmd & ESP_CMD_DMA));
+
+ sbus_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
+ sbus_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
+ if (esp->rev == FASHME) {
+ sbus_esp_write8(esp, (esp_count >> 16) & 0xff, FAS_RLO);
+ sbus_esp_write8(esp, 0, FAS_RHI);
+
+ scsi_esp_cmd(esp, cmd);
+
+ csr = esp->prev_hme_dmacsr;
+ csr |= DMA_SCSI_DISAB | DMA_ENABLE;
+ if (write)
+ csr |= DMA_ST_WRITE;
+ else
+ csr &= ~DMA_ST_WRITE;
+ esp->prev_hme_dmacsr = csr;
+
+ dma_write32(dma_count, DMA_COUNT);
+ dma_write32(addr, DMA_ADDR);
+ dma_write32(csr, DMA_CSR);
+ } else {
+ csr = dma_read32(DMA_CSR);
+ csr |= DMA_ENABLE;
+ if (write)
+ csr |= DMA_ST_WRITE;
+ else
+ csr &= ~DMA_ST_WRITE;
+ dma_write32(csr, DMA_CSR);
+ if (esp->dma->revision == dvmaesc1) {
+ u32 end = PAGE_ALIGN(addr + dma_count + 16U);
+ dma_write32(end - addr, DMA_COUNT);
+ }
+ dma_write32(addr, DMA_ADDR);
+
+ scsi_esp_cmd(esp, cmd);
+ }
+
+}
+
+static int sbus_esp_dma_error(struct esp *esp)
+{
+ u32 csr = dma_read32(DMA_CSR);
+
+ if (csr & DMA_HNDL_ERROR)
+ return 1;
+
+ return 0;
+}
+
+static const struct esp_driver_ops sbus_esp_ops = {
+ .esp_write8 = sbus_esp_write8,
+ .esp_read8 = sbus_esp_read8,
+ .map_single = sbus_esp_map_single,
+ .map_sg = sbus_esp_map_sg,
+ .unmap_single = sbus_esp_unmap_single,
+ .unmap_sg = sbus_esp_unmap_sg,
+ .irq_pending = sbus_esp_irq_pending,
+ .reset_dma = sbus_esp_reset_dma,
+ .dma_drain = sbus_esp_dma_drain,
+ .dma_invalidate = sbus_esp_dma_invalidate,
+ .send_dma_cmd = sbus_esp_send_dma_cmd,
+ .dma_error = sbus_esp_dma_error,
+};
+
+static int __devinit esp_sbus_probe_one(struct device *dev,
+ struct sbus_dev *esp_dev,
+ struct sbus_dev *espdma,
+ struct sbus_bus *sbus,
+ int hme)
+{
+ struct scsi_host_template *tpnt = &scsi_esp_template;
+ struct Scsi_Host *host;
+ struct esp *esp;
+ int err;
+
+ host = scsi_host_alloc(tpnt, sizeof(struct esp));
+
+ err = -ENOMEM;
+ if (!host)
+ goto fail;
+
+ host->max_id = (hme ? 16 : 8);
+ esp = host_to_esp(host);
+
+ esp->host = host;
+ esp->dev = esp_dev;
+ esp->ops = &sbus_esp_ops;
+
+ if (hme)
+ esp->flags |= ESP_FLAG_WIDE_CAPABLE;
+
+ err = esp_sbus_find_dma(esp, espdma);
+ if (err < 0)
+ goto fail_unlink;
+
+ err = esp_sbus_map_regs(esp, hme);
+ if (err < 0)
+ goto fail_unlink;
+
+ err = esp_sbus_map_command_block(esp);
+ if (err < 0)
+ goto fail_unmap_regs;
+
+ err = esp_sbus_register_irq(esp);
+ if (err < 0)
+ goto fail_unmap_command_block;
+
+ esp_sbus_get_props(esp, espdma);
+
+ /* Before we try to touch the ESP chip, ESC1 dma can
+ * come up with the reset bit set, so make sure that
+ * is clear first.
+ */
+ if (esp->dma->revision == dvmaesc1) {
+ u32 val = dma_read32(DMA_CSR);
+
+ dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
+ }
+
+ dev_set_drvdata(&esp_dev->ofdev.dev, esp);
+
+ err = scsi_esp_register(esp, dev);
+ if (err)
+ goto fail_free_irq;
+
+ return 0;
+
+fail_free_irq:
+ free_irq(host->irq, esp);
+fail_unmap_command_block:
+ sbus_free_consistent(esp->dev, 16,
+ esp->command_block,
+ esp->command_block_dma);
+fail_unmap_regs:
+ sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE);
+fail_unlink:
+ scsi_host_put(host);
+fail:
+ return err;
+}
+
+static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+{
+ struct sbus_dev *sdev = to_sbus_device(&dev->dev);
+ struct device_node *dp = dev->node;
+ struct sbus_dev *dma_sdev = NULL;
+ int hme = 0;
+
+ if (dp->parent &&
+ (!strcmp(dp->parent->name, "espdma") ||
+ !strcmp(dp->parent->name, "dma")))
+ dma_sdev = sdev->parent;
+ else if (!strcmp(dp->name, "SUNW,fas")) {
+ dma_sdev = sdev;
+ hme = 1;
+ }
+
+ return esp_sbus_probe_one(&dev->dev, sdev, dma_sdev,
+ sdev->bus, hme);
+}
+
+static int __devexit esp_sbus_remove(struct of_device *dev)
+{
+ struct esp *esp = dev_get_drvdata(&dev->dev);
+ unsigned int irq = esp->host->irq;
+ u32 val;
+
+ scsi_esp_unregister(esp);
+
+ /* Disable interrupts. */
+ val = dma_read32(DMA_CSR);
+ dma_write32(val & ~DMA_INT_ENAB, DMA_CSR);
+
+ free_irq(irq, esp);
+ sbus_free_consistent(esp->dev, 16,
+ esp->command_block,
+ esp->command_block_dma);
+ sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE);
+
+ scsi_host_put(esp->host);
+
+ return 0;
+}
+
+static struct of_device_id esp_match[] = {
+ {
+ .name = "SUNW,esp",
+ },
+ {
+ .name = "SUNW,fas",
+ },
+ {
+ .name = "esp",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, esp_match);
+
+static struct of_platform_driver esp_sbus_driver = {
+ .name = "esp",
+ .match_table = esp_match,
+ .probe = esp_sbus_probe,
+ .remove = __devexit_p(esp_sbus_remove),
+};
+
+static int __init sunesp_init(void)
+{
+ return of_register_driver(&esp_sbus_driver, &sbus_bus_type);
+}
+
+static void __exit sunesp_exit(void)
+{
+ of_unregister_driver(&esp_sbus_driver);
+}
+
+MODULE_DESCRIPTION("Sun ESP SCSI driver");
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(sunesp_init);
+module_exit(sunesp_exit);
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index c129a0e8e807..90621c3312bc 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1310,7 +1310,8 @@ static unsigned int check_modem_status(struct uart_8250_port *up)
{
unsigned int status = serial_in(up, UART_MSR);
- if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI) {
+ if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
+ up->port.info != NULL) {
if (status & UART_MSR_TERI)
up->port.icount.rng++;
if (status & UART_MSR_DDSR)
@@ -1333,8 +1334,9 @@ static inline void
serial8250_handle_port(struct uart_8250_port *up)
{
unsigned int status;
+ unsigned long flags;
- spin_lock(&up->port.lock);
+ spin_lock_irqsave(&up->port.lock, flags);
status = serial_inp(up, UART_LSR);
@@ -1346,7 +1348,7 @@ serial8250_handle_port(struct uart_8250_port *up)
if (status & UART_LSR_THRE)
transmit_chars(up);
- spin_unlock(&up->port.lock);
+ spin_unlock_irqrestore(&up->port.lock, flags);
}
/*
diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c
index 41431d0d5512..246c5572667b 100644
--- a/drivers/serial/icom.c
+++ b/drivers/serial/icom.c
@@ -164,7 +164,7 @@ static void free_port_memory(struct icom_port *icom_port)
}
}
-static int __init get_port_memory(struct icom_port *icom_port)
+static int __devinit get_port_memory(struct icom_port *icom_port)
{
int index;
unsigned long stgAddr;
@@ -1380,7 +1380,7 @@ static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *i
0x8024 + 2 - 2 * (icom_port->port - 2);
}
}
-static int __init icom_load_ports(struct icom_adapter *icom_adapter)
+static int __devinit icom_load_ports(struct icom_adapter *icom_adapter)
{
struct icom_port *icom_port;
int port_num;
@@ -1473,7 +1473,7 @@ static void icom_remove_adapter(struct icom_adapter *icom_adapter)
}
}
- free_irq(icom_adapter->irq_number, (void *) icom_adapter);
+ free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter);
iounmap(icom_adapter->base_addr);
icom_free_adapter(icom_adapter);
pci_release_regions(icom_adapter->pci_dev);
@@ -1539,7 +1539,6 @@ static int __devinit icom_probe(struct pci_dev *dev,
}
icom_adapter->base_addr_pci = pci_resource_start(dev, 0);
- icom_adapter->irq_number = dev->irq;
icom_adapter->pci_dev = dev;
icom_adapter->version = ent->driver_data;
icom_adapter->subsystem_id = ent->subdevice;
@@ -1570,7 +1569,7 @@ static int __devinit icom_probe(struct pci_dev *dev,
icom_port = &icom_adapter->port_info[index];
if (icom_port->status == ICOM_PORT_ACTIVE) {
- icom_port->uart_port.irq = icom_port->adapter->irq_number;
+ icom_port->uart_port.irq = icom_port->adapter->pci_dev->irq;
icom_port->uart_port.type = PORT_ICOM;
icom_port->uart_port.iotype = UPIO_MEM;
icom_port->uart_port.membase =
diff --git a/drivers/serial/icom.h b/drivers/serial/icom.h
index 798f1ef23712..e8578d8cd35e 100644
--- a/drivers/serial/icom.h
+++ b/drivers/serial/icom.h
@@ -258,7 +258,6 @@ struct icom_port {
struct icom_adapter {
void __iomem * base_addr;
unsigned long base_addr_pci;
- unsigned char irq_number;
struct pci_dev *pci_dev;
struct icom_port port_info[4];
int index;
diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c
index 96a852aa1903..bfd44177a215 100644
--- a/drivers/serial/sunsu.c
+++ b/drivers/serial/sunsu.c
@@ -1387,8 +1387,8 @@ static enum su_type __devinit su_get_type(struct device_node *dp)
struct device_node *ap = of_find_node_by_path("/aliases");
if (ap) {
- char *keyb = of_get_property(ap, "keyboard", NULL);
- char *ms = of_get_property(ap, "mouse", NULL);
+ const char *keyb = of_get_property(ap, "keyboard", NULL);
+ const char *ms = of_get_property(ap, "mouse", NULL);
if (keyb) {
if (dp == of_find_node_by_path(keyb))
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index 220abce63e4a..b10211c420ef 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -77,7 +77,7 @@ static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
switch (value) {
case BITBANG_CS_INACTIVE:
- hw->pdata->set_cs(hw->pdata, spi->chip_select, cspol^1);
+ hw->set_cs(hw->pdata, spi->chip_select, cspol^1);
break;
case BITBANG_CS_ACTIVE:
@@ -98,7 +98,7 @@ static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
/* write new configration */
writeb(spcon, hw->regs + S3C2410_SPCON);
- hw->pdata->set_cs(hw->pdata, spi->chip_select, cspol);
+ hw->set_cs(hw->pdata, spi->chip_select, cspol);
break;
}
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 8b7ff467d262..c1b0affae290 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -15,7 +15,6 @@ 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/
obj-$(CONFIG_USB_ACM) += class/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 3dfa3e40e148..30b7bfbc985a 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan
* Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
+ * Copyright (C) 2007 Simon Arlott
*
* 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
@@ -34,14 +35,14 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
-#include <linux/device.h> /* FIXME: linux/firmware.h should include it itself */
+#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/mutex.h>
#include "usbatm.h"
-#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands"
-#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott"
+#define DRIVER_VERSION "0.3"
#define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver"
static const char cxacru_driver_name[] = "cxacru";
@@ -64,7 +65,7 @@ static const char cxacru_driver_name[] = "cxacru";
#define SDRAM_ENA 0x1
#define CMD_TIMEOUT 2000 /* msecs */
-#define POLL_INTERVAL 5000 /* msecs */
+#define POLL_INTERVAL 1 /* secs */
/* commands for interaction with the modem through the control channel before
* firmware is loaded */
@@ -146,6 +147,13 @@ enum cxacru_info_idx {
CXINF_MAX = 0x1c,
};
+enum cxacru_poll_state {
+ CXPOLL_STOPPING,
+ CXPOLL_STOPPED,
+ CXPOLL_POLLING,
+ CXPOLL_SHUTDOWN
+};
+
struct cxacru_modem_type {
u32 pll_f_clk;
u32 pll_b_clk;
@@ -158,7 +166,12 @@ struct cxacru_data {
const struct cxacru_modem_type *modem_type;
int line_status;
+ struct mutex adsl_state_serialize;
+ int adsl_status;
struct delayed_work poll_work;
+ u32 card_info[CXINF_MAX];
+ struct mutex poll_state_serialize;
+ int poll_state;
/* contol handles */
struct mutex cm_serialize;
@@ -170,6 +183,275 @@ struct cxacru_data {
struct completion snd_done;
};
+static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
+ u8 *wdata, int wsize, u8 *rdata, int rsize);
+static void cxacru_poll_status(struct work_struct *work);
+
+/* Card info exported through sysfs */
+#define CXACRU__ATTR_INIT(_name) \
+static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
+
+#define CXACRU_CMD_INIT(_name) \
+static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
+
+#define CXACRU_ATTR_INIT(_value, _type, _name) \
+static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); \
+ struct cxacru_data *instance = usbatm_instance->driver_data; \
+ return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \
+} \
+CXACRU__ATTR_INIT(_name)
+
+#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+
+#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+
+static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
+{
+ if (unlikely(value < 0)) {
+ return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
+ value / 100, -value % 100);
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
+ value / 100, value % 100);
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf)
+{
+ switch (value) {
+ case 0: return snprintf(buf, PAGE_SIZE, "no\n");
+ case 1: return snprintf(buf, PAGE_SIZE, "yes\n");
+ default: return 0;
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf)
+{
+ switch (value) {
+ case 1: return snprintf(buf, PAGE_SIZE, "not connected\n");
+ case 2: return snprintf(buf, PAGE_SIZE, "connected\n");
+ case 3: return snprintf(buf, PAGE_SIZE, "lost\n");
+ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf)
+{
+ switch (value) {
+ case 0: return snprintf(buf, PAGE_SIZE, "down\n");
+ case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n");
+ case 2: return snprintf(buf, PAGE_SIZE, "training\n");
+ case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n");
+ case 4: return snprintf(buf, PAGE_SIZE, "exchange\n");
+ case 5: return snprintf(buf, PAGE_SIZE, "up\n");
+ case 6: return snprintf(buf, PAGE_SIZE, "waiting\n");
+ case 7: return snprintf(buf, PAGE_SIZE, "initialising\n");
+ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
+{
+ switch (value) {
+ case 0: return 0;
+ case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n");
+ case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n");
+ case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n");
+ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+ }
+}
+
+/*
+ * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since
+ * this data is already in atm_dev there's no point.
+ *
+ * MAC_ADDRESS_HIGH = 0x????5544
+ * MAC_ADDRESS_LOW = 0x33221100
+ * Where 00-55 are bytes 0-5 of the MAC.
+ */
+static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+ struct atm_dev *atm_dev = usbatm_instance->atm_dev;
+
+ return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2],
+ atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);
+}
+
+static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+ struct cxacru_data *instance = usbatm_instance->driver_data;
+ u32 value = instance->card_info[CXINF_LINE_STARTABLE];
+
+ switch (value) {
+ case 0: return snprintf(buf, PAGE_SIZE, "running\n");
+ case 1: return snprintf(buf, PAGE_SIZE, "stopped\n");
+ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
+ }
+}
+
+static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
+ struct cxacru_data *instance = usbatm_instance->driver_data;
+ int ret;
+ int poll = -1;
+ char str_cmd[8];
+ int len = strlen(buf);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ ret = sscanf(buf, "%7s", str_cmd);
+ if (ret != 1)
+ return -EINVAL;
+ ret = 0;
+
+ if (mutex_lock_interruptible(&instance->adsl_state_serialize))
+ return -ERESTARTSYS;
+
+ if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) {
+ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ atm_err(usbatm_instance, "change adsl state:"
+ " CHIP_ADSL_LINE_STOP returned %d\n", ret);
+
+ ret = -EIO;
+ } else {
+ ret = len;
+ poll = CXPOLL_STOPPED;
+ }
+ }
+
+ /* Line status is only updated every second
+ * and the device appears to only react to
+ * START/STOP every second too. Wait 1.5s to
+ * be sure that restart will have an effect. */
+ if (!strcmp(str_cmd, "restart"))
+ msleep(1500);
+
+ if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) {
+ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ atm_err(usbatm_instance, "change adsl state:"
+ " CHIP_ADSL_LINE_START returned %d\n", ret);
+
+ ret = -EIO;
+ } else {
+ ret = len;
+ poll = CXPOLL_POLLING;
+ }
+ }
+
+ if (!strcmp(str_cmd, "poll")) {
+ ret = len;
+ poll = CXPOLL_POLLING;
+ }
+
+ if (ret == 0) {
+ ret = -EINVAL;
+ poll = -1;
+ }
+
+ if (poll == CXPOLL_POLLING) {
+ mutex_lock(&instance->poll_state_serialize);
+ switch (instance->poll_state) {
+ case CXPOLL_STOPPED:
+ /* start polling */
+ instance->poll_state = CXPOLL_POLLING;
+ break;
+
+ case CXPOLL_STOPPING:
+ /* abort stop request */
+ instance->poll_state = CXPOLL_POLLING;
+ case CXPOLL_POLLING:
+ case CXPOLL_SHUTDOWN:
+ /* don't start polling */
+ poll = -1;
+ }
+ mutex_unlock(&instance->poll_state_serialize);
+ } else if (poll == CXPOLL_STOPPED) {
+ mutex_lock(&instance->poll_state_serialize);
+ /* request stop */
+ if (instance->poll_state == CXPOLL_POLLING)
+ instance->poll_state = CXPOLL_STOPPING;
+ mutex_unlock(&instance->poll_state_serialize);
+ }
+
+ mutex_unlock(&instance->adsl_state_serialize);
+
+ if (poll == CXPOLL_POLLING)
+ cxacru_poll_status(&instance->poll_work.work);
+
+ return ret;
+}
+
+/*
+ * All device attributes are included in CXACRU_ALL_FILES
+ * so that the same list can be used multiple times:
+ * INIT (define the device attributes)
+ * CREATE (create all the device files)
+ * REMOVE (remove all the device files)
+ *
+ * With the last two being defined as needed in the functions
+ * they are used in before calling CXACRU_ALL_FILES()
+ */
+#define CXACRU_ALL_FILES(_action) \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE, u32, downstream_rate); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE, u32, upstream_rate); \
+CXACRU_ATTR_##_action(CXINF_LINK_STATUS, LINK, link_status); \
+CXACRU_ATTR_##_action(CXINF_LINE_STATUS, LINE, line_status); \
+CXACRU__ATTR_##_action( mac_address); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN, dB, upstream_snr_margin); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN, dB, downstream_snr_margin); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION, dB, upstream_attenuation); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION, dB, downstream_attenuation); \
+CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER, s8, transmitter_power); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME, u32, upstream_bits_per_frame); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32, downstream_bits_per_frame); \
+CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS, u32, startup_attempts); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS, u32, upstream_crc_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS, u32, downstream_crc_errors); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS, u32, upstream_fec_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS, u32, downstream_fec_errors); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS, u32, upstream_hec_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS, u32, downstream_hec_errors); \
+CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \
+CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \
+CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \
+CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \
+CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \
+CXACRU_CMD_##_action( adsl_state);
+
+CXACRU_ALL_FILES(INIT);
+
/* the following three functions are stolen from drivers/usb/core/message.c */
static void cxacru_blocking_completion(struct urb *urb)
{
@@ -347,8 +629,6 @@ static int cxacru_card_status(struct cxacru_data *instance)
return 0;
}
-static void cxacru_poll_status(struct work_struct *work);
-
static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev)
{
@@ -357,6 +637,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev = usbatm_instance->atm_dev;
*/
int ret;
+ int start_polling = 1;
dbg("cxacru_atm_start");
@@ -369,14 +650,35 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
}
/* start ADSL */
+ mutex_lock(&instance->adsl_state_serialize);
ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
if (ret < 0) {
atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
+ mutex_unlock(&instance->adsl_state_serialize);
return ret;
}
/* Start status polling */
- cxacru_poll_status(&instance->poll_work.work);
+ mutex_lock(&instance->poll_state_serialize);
+ switch (instance->poll_state) {
+ case CXPOLL_STOPPED:
+ /* start polling */
+ instance->poll_state = CXPOLL_POLLING;
+ break;
+
+ case CXPOLL_STOPPING:
+ /* abort stop request */
+ instance->poll_state = CXPOLL_POLLING;
+ case CXPOLL_POLLING:
+ case CXPOLL_SHUTDOWN:
+ /* don't start polling */
+ start_polling = 0;
+ }
+ mutex_unlock(&instance->poll_state_serialize);
+ mutex_unlock(&instance->adsl_state_serialize);
+
+ if (start_polling)
+ cxacru_poll_status(&instance->poll_work.work);
return 0;
}
@@ -387,14 +689,46 @@ static void cxacru_poll_status(struct work_struct *work)
u32 buf[CXINF_MAX] = {};
struct usbatm_data *usbatm = instance->usbatm;
struct atm_dev *atm_dev = usbatm->atm_dev;
+ int keep_polling = 1;
int ret;
ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
if (ret < 0) {
- atm_warn(usbatm, "poll status: error %d\n", ret);
+ if (ret != -ESHUTDOWN)
+ atm_warn(usbatm, "poll status: error %d\n", ret);
+
+ mutex_lock(&instance->poll_state_serialize);
+ if (instance->poll_state != CXPOLL_SHUTDOWN) {
+ instance->poll_state = CXPOLL_STOPPED;
+
+ if (ret != -ESHUTDOWN)
+ atm_warn(usbatm, "polling disabled, set adsl_state"
+ " to 'start' or 'poll' to resume\n");
+ }
+ mutex_unlock(&instance->poll_state_serialize);
goto reschedule;
}
+ memcpy(instance->card_info, buf, sizeof(instance->card_info));
+
+ if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) {
+ instance->adsl_status = buf[CXINF_LINE_STARTABLE];
+
+ switch (instance->adsl_status) {
+ case 0:
+ atm_printk(KERN_INFO, usbatm, "ADSL state: running\n");
+ break;
+
+ case 1:
+ atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n");
+ break;
+
+ default:
+ atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status);
+ break;
+ }
+ }
+
if (instance->line_status == buf[CXINF_LINE_STATUS])
goto reschedule;
@@ -449,7 +783,20 @@ static void cxacru_poll_status(struct work_struct *work)
break;
}
reschedule:
- schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL));
+
+ mutex_lock(&instance->poll_state_serialize);
+ if (instance->poll_state == CXPOLL_STOPPING &&
+ instance->adsl_status == 1 && /* stopped */
+ instance->line_status == 0) /* down */
+ instance->poll_state = CXPOLL_STOPPED;
+
+ if (instance->poll_state == CXPOLL_STOPPED)
+ keep_polling = 0;
+ mutex_unlock(&instance->poll_state_serialize);
+
+ if (keep_polling)
+ schedule_delayed_work(&instance->poll_work,
+ round_jiffies_relative(POLL_INTERVAL*HZ));
}
static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
@@ -684,6 +1031,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
instance->usbatm = usbatm_instance;
instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
+ memset(instance->card_info, 0, sizeof(instance->card_info));
+
+ mutex_init(&instance->poll_state_serialize);
+ instance->poll_state = CXPOLL_STOPPED;
+ instance->line_status = -1;
+ instance->adsl_status = -1;
+
+ mutex_init(&instance->adsl_state_serialize);
instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
if (!instance->rcv_buf) {
@@ -710,6 +1065,13 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
goto fail;
}
+ #define CXACRU_DEVICE_CREATE_FILE(_name) \
+ ret = device_create_file(&intf->dev, &dev_attr_##_name); \
+ if (unlikely(ret)) \
+ goto fail_sysfs;
+ CXACRU_ALL_FILES(CREATE);
+ #undef CXACRU_DEVICE_CREATE_FILE
+
usb_fill_int_urb(instance->rcv_urb,
usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD),
instance->rcv_buf, PAGE_SIZE,
@@ -730,6 +1092,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
return 0;
+ fail_sysfs:
+ dbg("cxacru_bind: device_create_file failed (%d)\n", ret);
+
+ #define CXACRU_DEVICE_REMOVE_FILE(_name) \
+ device_remove_file(&intf->dev, &dev_attr_##_name);
+ CXACRU_ALL_FILES(REMOVE);
+ #undef CXACRU_DEVICE_REVOVE_FILE
+
fail:
free_page((unsigned long) instance->snd_buf);
free_page((unsigned long) instance->rcv_buf);
@@ -744,6 +1114,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
struct usb_interface *intf)
{
struct cxacru_data *instance = usbatm_instance->driver_data;
+ int is_polling = 1;
dbg("cxacru_unbind entered");
@@ -752,8 +1123,20 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
return;
}
- while (!cancel_delayed_work(&instance->poll_work))
- flush_scheduled_work();
+ mutex_lock(&instance->poll_state_serialize);
+ BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN);
+
+ /* ensure that status polling continues unless
+ * it has already stopped */
+ if (instance->poll_state == CXPOLL_STOPPED)
+ is_polling = 0;
+
+ /* stop polling from being stopped or started */
+ instance->poll_state = CXPOLL_SHUTDOWN;
+ mutex_unlock(&instance->poll_state_serialize);
+
+ if (is_polling)
+ cancel_rearming_delayed_work(&instance->poll_work);
usb_kill_urb(instance->snd_urb);
usb_kill_urb(instance->rcv_urb);
@@ -762,6 +1145,12 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
free_page((unsigned long) instance->snd_buf);
free_page((unsigned long) instance->rcv_buf);
+
+ #define CXACRU_DEVICE_REMOVE_FILE(_name) \
+ device_remove_file(&intf->dev, &dev_attr_##_name);
+ CXACRU_ALL_FILES(REMOVE);
+ #undef CXACRU_DEVICE_REVOVE_FILE
+
kfree(instance);
usbatm_instance->driver_data = NULL;
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index ec63b0ee0743..b3f779f5933a 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -274,6 +274,9 @@ static void usbatm_complete(struct urb *urb)
(!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
urb->status != -EILSEQ ))
{
+ if (urb->status == -ESHUTDOWN)
+ return;
+
if (printk_ratelimit())
atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n",
__func__, urb, urb->status);
@@ -343,7 +346,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end);
}
- memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+ memcpy(skb_tail_pointer(sarb), source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
__skb_put(sarb, ATM_CELL_PAYLOAD);
if (pti & 1) {
@@ -370,7 +373,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
goto out;
}
- if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+ if (crc32_be(~0, skb_tail_pointer(sarb) - pdu_length, pdu_length) != 0xc704dd7b) {
atm_rldbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n",
__func__, vcc);
atomic_inc(&vcc->stats->rx_err);
@@ -396,7 +399,9 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
goto out; /* atm_charge increments rx_drop */
}
- memcpy(skb->data, sarb->tail - pdu_length, length);
+ skb_copy_to_linear_data(skb,
+ skb_tail_pointer(sarb) - pdu_length,
+ length);
__skb_put(skb, length);
vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
@@ -484,7 +489,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
ptr[4] = 0xec;
ptr += ATM_CELL_HEADER;
- memcpy(ptr, skb->data, data_len);
+ skb_copy_from_linear_data(skb, ptr, data_len);
ptr += data_len;
__skb_pull(skb, data_len);
@@ -966,6 +971,14 @@ static int usbatm_atm_init(struct usbatm_data *instance)
/* temp init ATM device, set to 128kbit */
atm_dev->link_rate = 128 * 1000 / 424;
+ ret = sysfs_create_link(&atm_dev->class_dev.kobj,
+ &instance->usb_intf->dev.kobj, "device");
+ if (ret) {
+ atm_err(instance, "%s: sysfs_create_link failed: %d\n",
+ __func__, ret);
+ goto fail_sysfs;
+ }
+
if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) {
atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret);
goto fail;
@@ -984,6 +997,8 @@ static int usbatm_atm_init(struct usbatm_data *instance)
return 0;
fail:
+ sysfs_remove_link(&atm_dev->class_dev.kobj, "device");
+ fail_sysfs:
instance->atm_dev = NULL;
atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */
return ret;
@@ -1316,8 +1331,10 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
kfree(instance->cell_buf);
/* ATM finalize */
- if (instance->atm_dev)
+ if (instance->atm_dev) {
+ sysfs_remove_link(&instance->atm_dev->class_dev.kobj, "device");
atm_dev_deregister(instance->atm_dev);
+ }
usbatm_put_instance(instance); /* taken in usbatm_usb_probe */
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 31ae661e586a..14de3b1b6a20 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -212,7 +212,41 @@ static int acm_write_start(struct acm *acm)
}
return rc;
}
+/*
+ * attributes exported through sysfs
+ */
+static ssize_t show_caps
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct acm *acm = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d", acm->ctrl_caps);
+}
+static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
+
+static ssize_t show_country_codes
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct acm *acm = usb_get_intfdata(intf);
+
+ memcpy(buf, acm->country_codes, acm->country_code_size);
+ return acm->country_code_size;
+}
+
+static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
+
+static ssize_t show_country_rel_date
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct acm *acm = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d", acm->country_rel_date);
+}
+static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
/*
* Interrupt handlers for various ACM device responses
*/
@@ -514,6 +548,7 @@ static void acm_tty_unregister(struct acm *acm)
usb_free_urb(acm->writeurb);
for (i = 0; i < nr; i++)
usb_free_urb(acm->ru[i].urb);
+ kfree(acm->country_codes);
kfree(acm);
}
@@ -761,6 +796,7 @@ static int acm_probe (struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_cdc_union_desc *union_header = NULL;
+ struct usb_cdc_country_functional_desc *cfd = NULL;
char *buffer = intf->altsetting->extra;
int buflen = intf->altsetting->extralen;
struct usb_interface *control_interface;
@@ -824,8 +860,9 @@ static int acm_probe (struct usb_interface *intf,
union_header = (struct usb_cdc_union_desc *)
buffer;
break;
- case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */
- break; /* for now we ignore it */
+ case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
+ cfd = (struct usb_cdc_country_functional_desc *)buffer;
+ break;
case USB_CDC_HEADER_TYPE: /* maybe check version */
break; /* for now we ignore it */
case USB_CDC_ACM_TYPE:
@@ -983,6 +1020,34 @@ skip_normal_probe:
goto alloc_fail7;
}
+ usb_set_intfdata (intf, acm);
+
+ i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
+ if (i < 0)
+ goto alloc_fail8;
+
+ if (cfd) { /* export the country data */
+ acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
+ if (!acm->country_codes)
+ goto skip_countries;
+ acm->country_code_size = cfd->bLength - 4;
+ memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
+ acm->country_rel_date = cfd->iCountryCodeRelDate;
+
+ i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
+ if (i < 0) {
+ kfree(acm->country_codes);
+ goto skip_countries;
+ }
+
+ i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
+ if (i < 0) {
+ kfree(acm->country_codes);
+ goto skip_countries;
+ }
+ }
+
+skip_countries:
usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -1006,9 +1071,10 @@ skip_normal_probe:
tty_register_device(acm_tty_driver, minor, &control_interface->dev);
acm_table[minor] = acm;
- usb_set_intfdata (intf, acm);
- return 0;
+ return 0;
+alloc_fail8:
+ usb_free_urb(acm->writeurb);
alloc_fail7:
for (i = 0; i < num_rx_buf; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
@@ -1027,7 +1093,7 @@ alloc_fail:
static void acm_disconnect(struct usb_interface *intf)
{
- struct acm *acm = usb_get_intfdata (intf);
+ struct acm *acm = usb_get_intfdata(intf);
struct usb_device *usb_dev = interface_to_usbdev(intf);
int i;
@@ -1041,6 +1107,11 @@ static void acm_disconnect(struct usb_interface *intf)
mutex_unlock(&open_mutex);
return;
}
+ if (acm->country_codes){
+ device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
+ device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
+ }
+ device_remove_file(&intf->dev, &dev_attr_bmCapabilities);
acm->dev = NULL;
usb_set_intfdata(acm->control, NULL);
usb_set_intfdata(acm->data, NULL);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 1bcaea32cfc1..09f7765dbf8d 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -91,6 +91,9 @@ struct acm {
struct urb *ctrlurb, *writeurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma; /* dma handles of buffers */
+ u8 *country_codes; /* country codes from device */
+ unsigned int country_code_size; /* size of this buffer */
+ unsigned int country_rel_date; /* release date of version */
struct acm_wb wb[ACM_NW];
struct acm_ru ru[ACM_NR];
struct acm_rb rb[ACM_NR];
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 2fc0f88a3d86..f493fb1eaa27 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -31,7 +31,30 @@ config USB_DEVICEFS
For the format of the various /proc/bus/usb/ files, please read
<file:Documentation/usb/proc_usb_info.txt>.
- Most users want to say Y here.
+ Usbfs files can't handle Access Control Lists (ACL), which are the
+ default way to grant access to USB devices for untrusted users of a
+ desktop system. The usbfs functionality is replaced by real
+ device-nodes managed by udev. These nodes live in /dev/bus/usb and
+ are used by libusb.
+
+config USB_DEVICE_CLASS
+ bool "USB device class-devices (DEPRECATED)"
+ depends on USB
+ default n
+ ---help---
+ Userspace access to USB devices is granted by device-nodes exported
+ directly from the usbdev in sysfs. Old versions of the driver
+ core and udev needed additional class devices to export device nodes.
+
+ These additional devices are difficult to handle in userspace, if
+ information about USB interfaces must be available. One device contains
+ the device node, the other device contains the interface data. Both
+ devices are at the same level in sysfs (siblings) and one can't access
+ the other. The device node created directly by the usbdev is the parent
+ device of the interface and therefore easily accessible from the interface
+ event.
+
+ This option provides backward compatibility if needed.
config USB_DYNAMIC_MINORS
bool "Dynamic USB minor allocation (EXPERIMENTAL)"
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index aefc7987120d..6753ca059ee4 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -246,7 +246,6 @@ static char *usb_dump_interface_descriptor(char *start, char *end,
if (start > end)
return start;
- down_read(&usb_bus_type.subsys.rwsem);
if (iface) {
driver_name = (iface->dev.driver
? iface->dev.driver->name
@@ -263,7 +262,6 @@ static char *usb_dump_interface_descriptor(char *start, char *end,
desc->bInterfaceSubClass,
desc->bInterfaceProtocol,
driver_name);
- up_read(&usb_bus_type.subsys.rwsem);
return start;
}
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 36e7a843bf91..927a181120a9 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -57,7 +57,6 @@
#define USB_MAXBUS 64
#define USB_DEVICE_MAX USB_MAXBUS * 128
-static struct class *usb_device_class;
/* Mutual exclusion for removal, open, and release */
DEFINE_MUTEX(usbfs_mutex);
@@ -421,14 +420,11 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum)
if (test_bit(ifnum, &ps->ifclaimed))
return 0;
- /* lock against other changes to driver bindings */
- down_write(&usb_bus_type.subsys.rwsem);
intf = usb_ifnum_to_if(dev, ifnum);
if (!intf)
err = -ENOENT;
else
err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
- up_write(&usb_bus_type.subsys.rwsem);
if (err == 0)
set_bit(ifnum, &ps->ifclaimed);
return err;
@@ -444,8 +440,6 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
if (ifnum >= 8*sizeof(ps->ifclaimed))
return err;
dev = ps->dev;
- /* lock against other changes to driver bindings */
- down_write(&usb_bus_type.subsys.rwsem);
intf = usb_ifnum_to_if(dev, ifnum);
if (!intf)
err = -ENOENT;
@@ -453,7 +447,6 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
usb_driver_release_interface(&usbfs_driver, intf);
err = 0;
}
- up_write(&usb_bus_type.subsys.rwsem);
return err;
}
@@ -520,22 +513,25 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
return ret;
}
-static struct usb_device *usbdev_lookup_minor(int minor)
+static int __match_minor(struct device *dev, void *data)
{
- struct device *device;
- struct usb_device *udev = NULL;
+ int minor = *((int *)data);
- down(&usb_device_class->sem);
- list_for_each_entry(device, &usb_device_class->devices, node) {
- if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
- udev = device->platform_data;
- break;
- }
- }
- up(&usb_device_class->sem);
+ if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
+ return 1;
+ return 0;
+}
- return udev;
-};
+static struct usb_device *usbdev_lookup_by_minor(int minor)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor);
+ if (!dev)
+ return NULL;
+ put_device(dev);
+ return container_of(dev, struct usb_device, dev);
+}
/*
* file operations
@@ -554,11 +550,14 @@ static int usbdev_open(struct inode *inode, struct file *file)
goto out;
ret = -ENOENT;
- /* check if we are called from a real node or usbfs */
+ /* usbdev device-node */
if (imajor(inode) == USB_DEVICE_MAJOR)
- dev = usbdev_lookup_minor(iminor(inode));
+ dev = usbdev_lookup_by_minor(iminor(inode));
+#ifdef CONFIG_USB_DEVICEFS
+ /* procfs file */
if (!dev)
dev = inode->i_private;
+#endif
if (!dev)
goto out;
ret = usb_autoresume_device(dev);
@@ -581,7 +580,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
ps->disccontext = NULL;
ps->ifclaimed = 0;
security_task_getsecid(current, &ps->secid);
- wmb();
+ smp_wmb();
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
out:
@@ -813,7 +812,6 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
if (copy_from_user(&gd, arg, sizeof(gd)))
return -EFAULT;
- down_read(&usb_bus_type.subsys.rwsem);
intf = usb_ifnum_to_if(ps->dev, gd.interface);
if (!intf || !intf->dev.driver)
ret = -ENODATA;
@@ -822,7 +820,6 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
sizeof(gd.driver));
ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
}
- up_read(&usb_bus_type.subsys.rwsem);
return ret;
}
@@ -1351,15 +1348,12 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
/* disconnect kernel driver from interface */
case USBDEVFS_DISCONNECT:
-
- down_write(&usb_bus_type.subsys.rwsem);
if (intf->dev.driver) {
driver = to_usb_driver(intf->dev.driver);
dev_dbg (&intf->dev, "disconnect by usbfs\n");
usb_driver_release_interface(driver, intf);
} else
retval = -ENODATA;
- up_write(&usb_bus_type.subsys.rwsem);
break;
/* let kernel drivers try to (re)bind to the interface */
@@ -1371,7 +1365,6 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
/* talk directly to the interface's driver */
default:
- down_read(&usb_bus_type.subsys.rwsem);
if (intf->dev.driver)
driver = to_usb_driver(intf->dev.driver);
if (driver == NULL || driver->ioctl == NULL) {
@@ -1381,7 +1374,6 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
if (retval == -ENOIOCTLCMD)
retval = -ENOTTY;
}
- up_read(&usb_bus_type.subsys.rwsem);
}
/* cleanup and return */
@@ -1583,7 +1575,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai
return mask;
}
-const struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbdev_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
@@ -1592,50 +1584,53 @@ const struct file_operations usbfs_device_file_operations = {
.release = usbdev_release,
};
-static int usbdev_add(struct usb_device *dev)
+#ifdef CONFIG_USB_DEVICE_CLASS
+static struct class *usb_classdev_class;
+
+static int usb_classdev_add(struct usb_device *dev)
{
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
- dev->usbfs_dev = device_create(usb_device_class, &dev->dev,
+ dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
MKDEV(USB_DEVICE_MAJOR, minor),
"usbdev%d.%d", dev->bus->busnum, dev->devnum);
- if (IS_ERR(dev->usbfs_dev))
- return PTR_ERR(dev->usbfs_dev);
+ if (IS_ERR(dev->usb_classdev))
+ return PTR_ERR(dev->usb_classdev);
- dev->usbfs_dev->platform_data = dev;
return 0;
}
-static void usbdev_remove(struct usb_device *dev)
+static void usb_classdev_remove(struct usb_device *dev)
{
- device_unregister(dev->usbfs_dev);
+ device_unregister(dev->usb_classdev);
}
-static int usbdev_notify(struct notifier_block *self, unsigned long action,
- void *dev)
+static int usb_classdev_notify(struct notifier_block *self,
+ unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
- if (usbdev_add(dev))
+ if (usb_classdev_add(dev))
return NOTIFY_BAD;
break;
case USB_DEVICE_REMOVE:
- usbdev_remove(dev);
+ usb_classdev_remove(dev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block usbdev_nb = {
- .notifier_call = usbdev_notify,
+ .notifier_call = usb_classdev_notify,
};
+#endif
static struct cdev usb_device_cdev = {
.kobj = {.name = "usb_device", },
.owner = THIS_MODULE,
};
-int __init usbdev_init(void)
+int __init usb_devio_init(void)
{
int retval;
@@ -1645,38 +1640,38 @@ int __init usbdev_init(void)
err("unable to register minors for usb_device");
goto out;
}
- cdev_init(&usb_device_cdev, &usbfs_device_file_operations);
+ cdev_init(&usb_device_cdev, &usbdev_file_operations);
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
if (retval) {
err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
goto error_cdev;
}
- usb_device_class = class_create(THIS_MODULE, "usb_device");
- if (IS_ERR(usb_device_class)) {
+#ifdef CONFIG_USB_DEVICE_CLASS
+ usb_classdev_class = class_create(THIS_MODULE, "usb_device");
+ if (IS_ERR(usb_classdev_class)) {
err("unable to register usb_device class");
- retval = PTR_ERR(usb_device_class);
- goto error_class;
+ retval = PTR_ERR(usb_classdev_class);
+ cdev_del(&usb_device_cdev);
+ usb_classdev_class = NULL;
+ goto out;
}
usb_register_notify(&usbdev_nb);
-
+#endif
out:
return retval;
-error_class:
- usb_device_class = NULL;
- cdev_del(&usb_device_cdev);
-
error_cdev:
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
goto out;
}
-void usbdev_cleanup(void)
+void usb_devio_cleanup(void)
{
+#ifdef CONFIG_USB_DEVICE_CLASS
usb_unregister_notify(&usbdev_nb);
- class_destroy(usb_device_class);
+ class_destroy(usb_classdev_class);
+#endif
cdev_del(&usb_device_cdev);
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
}
-
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 9e3e943f313c..b9f7f90aef82 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -287,9 +287,9 @@ static int usb_unbind_interface(struct device *dev)
* 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.
+ * Callers must own the device lock, so driver probe() entries don't need
+ * extra locking, but other call contexts may need to explicitly claim that
+ * lock.
*/
int usb_driver_claim_interface(struct usb_driver *driver,
struct usb_interface *iface, void* priv)
@@ -330,9 +330,9 @@ EXPORT_SYMBOL(usb_driver_claim_interface);
* 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.
+ * Callers must own the device lock, so driver disconnect() entries don't
+ * need extra locking, but other call contexts may need to explicitly claim
+ * that lock.
*/
void usb_driver_release_interface(struct usb_driver *driver,
struct usb_interface *iface)
@@ -574,23 +574,10 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
}
#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_device *usb_dev;
- struct usb_host_interface *alt;
int i = 0;
int length = 0;
@@ -600,13 +587,11 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
/* driver is often null here; dev_dbg() would oops */
pr_debug ("usb %s: uevent\n", dev->bus_id);
- if (is_usb_device(dev)) {
+ if (is_usb_device(dev))
usb_dev = to_usb_device(dev);
- alt = NULL;
- } else {
- intf = to_usb_interface(dev);
+ else {
+ struct usb_interface *intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev(intf);
- alt = intf->cur_altsetting;
}
if (usb_dev->devnum < 0) {
@@ -621,9 +606,7 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
#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
+ * act as usermode drivers.
*/
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
@@ -650,44 +633,29 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
usb_dev->descriptor.bDeviceProtocol))
return -ENOMEM;
- if (!is_usb_device(dev)) {
-
- if (add_uevent_var(envp, num_envp, &i,
+ 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;
+ "BUSNUM=%03d",
+ usb_dev->bus->busnum))
+ return -ENOMEM;
- if (add_uevent_var(envp, num_envp, &i,
+ 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;
- }
+ "DEVNUM=%03d",
+ usb_dev->devnum))
+ return -ENOMEM;
envp[i] = NULL;
-
return 0;
}
#else
static int usb_uevent(struct device *dev, char **envp,
- int num_envp, char *buffer, int buffer_size)
+ int num_envp, char *buffer, int buffer_size)
{
return -ENODEV;
}
-
#endif /* CONFIG_HOTPLUG */
/**
@@ -872,8 +840,10 @@ static int usb_resume_device(struct usb_device *udev)
done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
- if (status == 0)
+ if (status == 0) {
+ udev->autoresume_disabled = 0;
udev->dev.power.power_state.event = PM_EVENT_ON;
+ }
return status;
}
@@ -962,6 +932,7 @@ static int autosuspend_check(struct usb_device *udev)
{
int i;
struct usb_interface *intf;
+ unsigned long suspend_time;
/* For autosuspend, fail fast if anything is in use or autosuspend
* is disabled. Also fail if any interfaces require remote wakeup
@@ -970,9 +941,10 @@ static int autosuspend_check(struct usb_device *udev)
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->pm_usage_cnt > 0)
return -EBUSY;
- if (!udev->autosuspend_delay)
+ if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled)
return -EPERM;
+ suspend_time = udev->last_busy + udev->autosuspend_delay;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
@@ -988,6 +960,24 @@ static int autosuspend_check(struct usb_device *udev)
}
}
}
+
+ /* If everything is okay but the device hasn't been idle for long
+ * enough, queue a delayed autosuspend request.
+ */
+ if (time_after(suspend_time, jiffies)) {
+ if (!timer_pending(&udev->autosuspend.timer)) {
+
+ /* The value of jiffies may change between the
+ * time_after() comparison above and the subtraction
+ * below. That's okay; the system behaves sanely
+ * when a timer is registered for the present moment
+ * or for the past.
+ */
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+ suspend_time - jiffies);
+ }
+ return -EAGAIN;
+ }
return 0;
}
@@ -1033,26 +1023,25 @@ static int autosuspend_check(struct usb_device *udev)
*
* This routine can run only in process context.
*/
-int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
+static 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;
+ if (udev->state == USB_STATE_NOTATTACHED ||
+ udev->state == USB_STATE_SUSPENDED)
+ goto done;
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->auto_pm) {
status = autosuspend_check(udev);
if (status < 0)
- return status;
+ goto done;
}
+ cancel_delayed_work(&udev->autosuspend);
/* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {
@@ -1077,6 +1066,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
} else if (parent)
usb_autosuspend_device(parent);
+ done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
}
@@ -1109,7 +1099,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*
* This routine can run only in process context.
*/
-int usb_resume_both(struct usb_device *udev)
+static int usb_resume_both(struct usb_device *udev)
{
int status = 0;
int i;
@@ -1117,11 +1107,17 @@ int usb_resume_both(struct usb_device *udev)
struct usb_device *parent = udev->parent;
cancel_delayed_work(&udev->autosuspend);
- if (udev->state == USB_STATE_NOTATTACHED)
- return -ENODEV;
+ if (udev->state == USB_STATE_NOTATTACHED) {
+ status = -ENODEV;
+ goto done;
+ }
/* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) {
+ if (udev->auto_pm && udev->autoresume_disabled) {
+ status = -EPERM;
+ goto done;
+ }
if (parent) {
status = usb_autoresume_device(parent);
if (status == 0) {
@@ -1167,6 +1163,7 @@ int usb_resume_both(struct usb_device *udev)
}
}
+ done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
}
@@ -1181,20 +1178,34 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
int status = 0;
usb_pm_lock(udev);
+ udev->auto_pm = 1;
udev->pm_usage_cnt += inc_usage_cnt;
WARN_ON(udev->pm_usage_cnt < 0);
if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) {
- udev->auto_pm = 1;
- status = usb_resume_both(udev);
+ if (udev->state == USB_STATE_SUSPENDED)
+ status = usb_resume_both(udev);
if (status != 0)
udev->pm_usage_cnt -= inc_usage_cnt;
- } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
- queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
- udev->autosuspend_delay);
+ else if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) {
+ if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ status = usb_suspend_both(udev, PMSG_SUSPEND);
+ }
usb_pm_unlock(udev);
return status;
}
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+void usb_autosuspend_work(struct work_struct *work)
+{
+ struct usb_device *udev =
+ container_of(work, struct usb_device, autosuspend.work);
+
+ usb_autopm_do_device(udev, 0);
+}
+
/**
* usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
* @udev: the usb_device to autosuspend
@@ -1286,15 +1297,20 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
if (intf->condition == USB_INTERFACE_UNBOUND)
status = -ENODEV;
else {
+ udev->auto_pm = 1;
intf->pm_usage_cnt += inc_usage_cnt;
if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) {
- udev->auto_pm = 1;
- status = usb_resume_both(udev);
+ if (udev->state == USB_STATE_SUSPENDED)
+ status = usb_resume_both(udev);
if (status != 0)
intf->pm_usage_cnt -= inc_usage_cnt;
- } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
- queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
- udev->autosuspend_delay);
+ else if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
+ if (inc_usage_cnt)
+ udev->last_busy = jiffies;
+ status = usb_suspend_both(udev, PMSG_SUSPEND);
+ }
}
usb_pm_unlock(udev);
return status;
@@ -1353,11 +1369,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
* 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).
+ *
+ * The routine increments @intf's usage counter. (However if the
+ * autoresume fails then the counter is re-decremented.) 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
@@ -1405,50 +1424,96 @@ int usb_autopm_set_interface(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
+#else
+
+void usb_autosuspend_work(struct work_struct *work)
+{}
+
#endif /* CONFIG_USB_SUSPEND */
-static int usb_suspend(struct device *dev, pm_message_t message)
+/**
+ * usb_external_suspend_device - external suspend of a USB device and its interfaces
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * This routine handles external suspend requests: ones not generated
+ * internally by a USB driver (autosuspend) but rather coming from the user
+ * (via sysfs) or the PM core (system sleep). The suspend will be carried
+ * out regardless of @udev's usage counter or those of its interfaces,
+ * and regardless of whether or not remote wakeup is enabled. Of course,
+ * interface drivers still have the option of failing the suspend (if
+ * there are unsuspended children, for example).
+ *
+ * The caller must hold @udev's device lock.
+ */
+int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
{
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;
+ usb_pm_lock(udev);
+ udev->auto_pm = 0;
+ status = usb_suspend_both(udev, msg);
+ usb_pm_unlock(udev);
return status;
}
-static int usb_resume(struct device *dev)
+/**
+ * usb_external_resume_device - external resume of a USB device and its interfaces
+ * @udev: the usb_device to resume
+ *
+ * This routine handles external resume requests: ones not generated
+ * internally by a USB driver (autoresume) but rather coming from the user
+ * (via sysfs), the PM core (system resume), or the device itself (remote
+ * wakeup). @udev's usage counter is unaffected.
+ *
+ * The caller must hold @udev's device lock.
+ */
+int usb_external_resume_device(struct usb_device *udev)
{
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);
+ 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;
+ /* Now that the device is awake, we can start trying to autosuspend
+ * it again. */
+ if (status == 0)
+ usb_try_autosuspend_device(udev);
return status;
}
+static int usb_suspend(struct device *dev, pm_message_t message)
+{
+ if (!is_usb_device(dev)) /* Ignore PM for interfaces */
+ return 0;
+ return usb_external_suspend_device(to_usb_device(dev), message);
+}
+
+static int usb_resume(struct device *dev)
+{
+ struct usb_device *udev;
+
+ if (!is_usb_device(dev)) /* Ignore PM for interfaces */
+ return 0;
+ udev = to_usb_device(dev);
+ if (udev->autoresume_disabled)
+ return -EPERM;
+ return usb_external_resume_device(udev);
+}
+
+#else
+
+#define usb_suspend NULL
+#define usb_resume NULL
+
#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/hcd.c b/drivers/usb/core/hcd.c
index b26c19e8d19f..40cf882293e6 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -37,6 +37,7 @@
#include <asm/irq.h>
#include <asm/byteorder.h>
#include <linux/platform_device.h>
+#include <linux/workqueue.h>
#include <linux/usb.h>
@@ -544,6 +545,8 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
unsigned long flags;
char buffer[4]; /* Any root hubs with > 31 ports? */
+ if (unlikely(!hcd->rh_registered))
+ return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
@@ -1296,14 +1299,26 @@ int hcd_bus_resume (struct usb_bus *bus)
return status;
}
+/* Workqueue routine for root-hub remote wakeup */
+static void hcd_resume_work(struct work_struct *work)
+{
+ struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
+ struct usb_device *udev = hcd->self.root_hub;
+
+ usb_lock_device(udev);
+ usb_mark_last_busy(udev);
+ usb_external_resume_device(udev);
+ usb_unlock_device(udev);
+}
+
/**
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
*
* The USB host controller calls this function when its root hub is
* suspended (with the remote wakeup feature enabled) and a remote
- * wakeup request is received. It queues a request for khubd to
- * resume the root hub (that is, manage its downstream ports again).
+ * wakeup request is received. The routine submits a workqueue request
+ * to resume the root hub (that is, manage its downstream ports again).
*/
void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
{
@@ -1311,7 +1326,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
spin_lock_irqsave (&hcd_root_hub_lock, flags);
if (hcd->rh_registered)
- usb_resume_root_hub (hcd->self.root_hub);
+ queue_work(ksuspend_usb_wq, &hcd->wakeup_work);
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
}
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
@@ -1500,6 +1515,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
+#ifdef CONFIG_PM
+ INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
+#endif
hcd->driver = driver;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
@@ -1666,16 +1684,20 @@ void usb_remove_hcd(struct usb_hcd *hcd)
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
+#ifdef CONFIG_PM
+ flush_workqueue(ksuspend_usb_wq);
+#endif
+
mutex_lock(&usb_bus_list_lock);
usb_disconnect(&hcd->self.root_hub);
mutex_unlock(&usb_bus_list_lock);
- hcd->poll_rh = 0;
- del_timer_sync(&hcd->rh_timer);
-
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
+ hcd->poll_rh = 0;
+ del_timer_sync(&hcd->rh_timer);
+
if (hcd->irq >= 0)
free_irq(hcd->irq, hcd);
usb_deregister_bus(&hcd->self);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 2a269ca20517..ef50fa494e47 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -68,6 +68,9 @@ struct usb_hcd {
struct timer_list rh_timer; /* drives root-hub polling */
struct urb *status_urb; /* the current status urb */
+#ifdef CONFIG_PM
+ struct work_struct wakeup_work; /* for remote wakeup */
+#endif
/*
* hardware info/state
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b89a98e61323..bde29ab2b504 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -119,8 +119,7 @@ MODULE_PARM_DESC(use_both_schemes,
"first one fails");
-#ifdef DEBUG
-static inline char *portspeed (int portstatus)
+static inline char *portspeed(int portstatus)
{
if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
return "480 Mb/s";
@@ -129,7 +128,6 @@ static inline char *portspeed (int portstatus)
else
return "12 Mb/s";
}
-#endif
/* Note that hdev or one of its children must be locked! */
static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev)
@@ -1369,11 +1367,15 @@ int usb_new_device(struct usb_device *udev)
}
#endif
+ /* export the usbdev device-node for libusb */
+ udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
+ (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
+
/* Register the device. The device driver is responsible
- * for adding the device files to usbfs and sysfs and for
- * configuring the device.
+ * for adding the device files to sysfs and for configuring
+ * the device.
*/
- err = device_add (&udev->dev);
+ err = device_add(&udev->dev);
if (err) {
dev_err(&udev->dev, "can't device_add, error %d\n", err);
goto fail;
@@ -1857,12 +1859,8 @@ static int remote_wakeup(struct usb_device *udev)
usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
- status = usb_autoresume_device(udev);
-
- /* Give the interface drivers a chance to do something,
- * then autosuspend the device again. */
- if (status == 0)
- usb_autosuspend_device(udev);
+ usb_mark_last_busy(udev);
+ status = usb_external_resume_device(udev);
}
usb_unlock_device(udev);
return status;
@@ -1986,13 +1984,6 @@ static inline int remote_wakeup(struct usb_device *udev)
#define hub_resume NULL
#endif
-void usb_resume_root_hub(struct usb_device *hdev)
-{
- struct usb_hub *hub = hdev_to_hub(hdev);
-
- kick_khubd(hub);
-}
-
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 11dad22da41c..cddfc62c4611 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -662,7 +662,7 @@ static void usbfs_add_device(struct usb_device *dev)
sprintf (name, "%03d", dev->devnum);
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
dev->bus->usbfs_dentry, dev,
- &usbfs_device_file_operations,
+ &usbdev_file_operations,
devuid, devgid);
if (dev->usbfs_dentry == NULL) {
err ("error creating usbfs device entry");
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 217a3d6d0a06..b7434787db5f 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -412,10 +412,24 @@ int usb_sg_init (
io->urbs [i]->status = -EINPROGRESS;
io->urbs [i]->actual_length = 0;
+ /*
+ * Some systems need to revert to PIO when DMA is temporarily
+ * unavailable. For their sakes, both transfer_buffer and
+ * transfer_dma are set when possible. However this can only
+ * work on systems without HIGHMEM, since DMA buffers located
+ * in high memory are not directly addressable by the CPU for
+ * PIO ... so when HIGHMEM is in use, transfer_buffer is NULL
+ * to prevent stale pointers and to help spot bugs.
+ */
if (dma) {
- /* hc may use _only_ transfer_dma */
io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
len = sg_dma_len (sg + i);
+#ifdef CONFIG_HIGHMEM
+ io->urbs[i]->transfer_buffer = NULL;
+#else
+ io->urbs[i]->transfer_buffer =
+ page_address(sg[i].page) + sg[i].offset;
+#endif
} else {
/* hc may use _only_ transfer_buffer */
io->urbs [i]->transfer_buffer =
@@ -1305,7 +1319,7 @@ int usb_reset_configuration(struct usb_device *dev)
return 0;
}
-static void release_interface(struct device *dev)
+void usb_release_interface(struct device *dev)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_interface_cache *intfc =
@@ -1315,6 +1329,67 @@ static void release_interface(struct device *dev)
kfree(intf);
}
+#ifdef CONFIG_HOTPLUG
+static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct usb_device *usb_dev;
+ struct usb_interface *intf;
+ 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);
+
+ intf = to_usb_interface(dev);
+ usb_dev = interface_to_usbdev(intf);
+ alt = intf->cur_altsetting;
+
+ 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;
+ return 0;
+}
+
+#else
+
+static int usb_if_uevent(struct device *dev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_HOTPLUG */
+
+struct device_type usb_if_device_type = {
+ .name = "usb_interface",
+ .release = usb_release_interface,
+ .uevent = usb_if_uevent,
+};
+
/*
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
@@ -1349,7 +1424,7 @@ static void release_interface(struct device *dev)
*
* This call is synchronous. The calling context must be able to sleep,
* must own the device lock, and must not hold the driver model's USB
- * bus rwsem; usb device driver probe() methods cannot use this routine.
+ * bus mutex; usb device driver probe() methods cannot use this routine.
*
* Returns zero on success, or else the status code returned by the
* underlying call that failed. On successful completion, each interface
@@ -1478,8 +1553,8 @@ free_interfaces:
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
+ intf->dev.type = &usb_if_device_type;
intf->dev.dma_mask = dev->dev.dma_mask;
- intf->dev.release = release_interface;
device_initialize (&intf->dev);
mark_quiesced(intf);
sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index f08ec85a6d64..739f520908aa 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -42,7 +42,7 @@ static void usb_autosuspend_quirk(struct usb_device *udev)
{
#ifdef CONFIG_USB_SUSPEND
/* disable autosuspend, but allow the user to re-enable it via sysfs */
- udev->autosuspend_delay = 0;
+ udev->autosuspend_disabled = 1;
#endif
}
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 311d5df80386..e7c982377488 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
+#include <linux/string.h>
#include <linux/usb.h>
#include "usb.h"
@@ -117,6 +118,16 @@ show_speed(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
static ssize_t
+show_busnum(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev;
+
+ udev = to_usb_device(dev);
+ return sprintf(buf, "%d\n", udev->bus->busnum);
+}
+static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL);
+
+static ssize_t
show_devnum(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_device *udev;
@@ -165,7 +176,7 @@ show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
- return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ);
+ return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ);
}
static ssize_t
@@ -173,39 +184,115 @@ set_autosuspend(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
- unsigned value, old;
+ int value;
- if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ)
+ if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ ||
+ value <= - INT_MAX/HZ)
return -EINVAL;
value *= HZ;
- old = udev->autosuspend_delay;
udev->autosuspend_delay = value;
- if (value > 0 && old == 0)
+ if (value >= 0)
usb_try_autosuspend_device(udev);
-
+ else {
+ if (usb_autoresume_device(udev) == 0)
+ usb_autosuspend_device(udev);
+ }
return count;
}
static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
show_autosuspend, set_autosuspend);
+static const char on_string[] = "on";
+static const char auto_string[] = "auto";
+static const char suspend_string[] = "suspend";
+
+static ssize_t
+show_level(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ const char *p = auto_string;
+
+ if (udev->state == USB_STATE_SUSPENDED) {
+ if (udev->autoresume_disabled)
+ p = suspend_string;
+ } else {
+ if (udev->autosuspend_disabled)
+ p = on_string;
+ }
+ return sprintf(buf, "%s\n", p);
+}
+
+static ssize_t
+set_level(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ int len = count;
+ char *cp;
+ int rc = 0;
+
+ cp = memchr(buf, '\n', count);
+ if (cp)
+ len = cp - buf;
+
+ usb_lock_device(udev);
+
+ /* Setting the flags without calling usb_pm_lock is a subject to
+ * races, but who cares...
+ */
+ if (len == sizeof on_string - 1 &&
+ strncmp(buf, on_string, len) == 0) {
+ udev->autosuspend_disabled = 1;
+ udev->autoresume_disabled = 0;
+ rc = usb_external_resume_device(udev);
+
+ } else if (len == sizeof auto_string - 1 &&
+ strncmp(buf, auto_string, len) == 0) {
+ udev->autosuspend_disabled = 0;
+ udev->autoresume_disabled = 0;
+ rc = usb_external_resume_device(udev);
+
+ } else if (len == sizeof suspend_string - 1 &&
+ strncmp(buf, suspend_string, len) == 0) {
+ udev->autosuspend_disabled = 0;
+ udev->autoresume_disabled = 1;
+ rc = usb_external_suspend_device(udev, PMSG_SUSPEND);
+
+ } else
+ rc = -EINVAL;
+
+ usb_unlock_device(udev);
+ return (rc < 0 ? rc : count);
+}
+
+static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
+
static char power_group[] = "power";
static int add_power_attributes(struct device *dev)
{
int rc = 0;
- if (is_usb_device(dev))
+ if (is_usb_device(dev)) {
rc = sysfs_add_file_to_group(&dev->kobj,
&dev_attr_autosuspend.attr,
power_group);
+ if (rc == 0)
+ rc = sysfs_add_file_to_group(&dev->kobj,
+ &dev_attr_level.attr,
+ power_group);
+ }
return rc;
}
static void remove_power_attributes(struct device *dev)
{
sysfs_remove_file_from_group(&dev->kobj,
+ &dev_attr_level.attr,
+ power_group);
+ sysfs_remove_file_from_group(&dev->kobj,
&dev_attr_autosuspend.attr,
power_group);
}
@@ -270,6 +357,7 @@ static struct attribute *dev_attrs[] = {
&dev_attr_bNumConfigurations.attr,
&dev_attr_bMaxPacketSize0.attr,
&dev_attr_speed.attr,
+ &dev_attr_busnum.attr,
&dev_attr_devnum.attr,
&dev_attr_version.attr,
&dev_attr_maxchild.attr,
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 54b42ce311c1..dfd1b5c87ca3 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -49,12 +49,13 @@ const char *usbcore_name = "usbcore";
static int nousb; /* Disable USB when built into kernel image */
-struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */
+/* Workqueue for autosuspend and for remote wakeup of root hubs */
+struct workqueue_struct *ksuspend_usb_wq;
#ifdef CONFIG_USB_SUSPEND
static int usb_autosuspend_delay = 2; /* Default delay value,
* in seconds */
-module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644);
+module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
#else
@@ -196,6 +197,11 @@ static void usb_release_dev(struct device *dev)
kfree(udev);
}
+struct device_type usb_device_type = {
+ .name = "usb_device",
+ .release = usb_release_dev,
+};
+
#ifdef CONFIG_PM
static int ksuspend_usb_init(void)
@@ -211,27 +217,6 @@ static void ksuspend_usb_cleanup(void)
destroy_workqueue(ksuspend_usb_wq);
}
-#ifdef CONFIG_USB_SUSPEND
-
-/* usb_autosuspend_work - callback routine to autosuspend a USB device */
-static void usb_autosuspend_work(struct work_struct *work)
-{
- struct usb_device *udev =
- container_of(work, struct usb_device, autosuspend.work);
-
- usb_pm_lock(udev);
- udev->auto_pm = 1;
- usb_suspend_both(udev, PMSG_SUSPEND);
- usb_pm_unlock(udev);
-}
-
-#else
-
-static void usb_autosuspend_work(struct work_struct *work)
-{}
-
-#endif /* CONFIG_USB_SUSPEND */
-
#else
#define ksuspend_usb_init() 0
@@ -267,13 +252,10 @@ 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.type = &usb_device_type;
dev->dev.dma_mask = bus->controller->dma_mask;
- 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;
@@ -902,9 +884,9 @@ static int __init usb_init(void)
retval = usb_register(&usbfs_driver);
if (retval)
goto driver_register_failed;
- retval = usbdev_init();
+ retval = usb_devio_init();
if (retval)
- goto usbdevice_init_failed;
+ goto usb_devio_init_failed;
retval = usbfs_init();
if (retval)
goto fs_init_failed;
@@ -919,8 +901,8 @@ static int __init usb_init(void)
hub_init_failed:
usbfs_cleanup();
fs_init_failed:
- usbdev_cleanup();
-usbdevice_init_failed:
+ usb_devio_cleanup();
+usb_devio_init_failed:
usb_deregister(&usbfs_driver);
driver_register_failed:
usb_major_cleanup();
@@ -947,7 +929,7 @@ static void __exit usb_exit(void)
usb_major_cleanup();
usbfs_cleanup();
usb_deregister(&usbfs_driver);
- usbdev_cleanup();
+ usb_devio_cleanup();
usb_hub_cleanup();
usb_host_cleanup();
bus_unregister(&usb_bus_type);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 08b5a04e3755..bf2eb0dae2ec 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -21,7 +21,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_resume_root_hub(struct usb_device *dev);
extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id);
@@ -34,10 +33,12 @@ extern void usb_host_cleanup(void);
#ifdef CONFIG_PM
-extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
-extern int usb_resume_both(struct usb_device *udev);
+extern void usb_autosuspend_work(struct work_struct *work);
extern int usb_port_suspend(struct usb_device *dev);
extern int usb_port_resume(struct usb_device *dev);
+extern int usb_external_suspend_device(struct usb_device *udev,
+ pm_message_t msg);
+extern int usb_external_resume_device(struct usb_device *udev);
static inline void usb_pm_lock(struct usb_device *udev)
{
@@ -51,11 +52,6 @@ static inline void usb_pm_unlock(struct usb_device *udev)
#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) {}
@@ -82,15 +78,13 @@ static inline int usb_autoresume_device(struct usb_device *udev)
extern struct workqueue_struct *ksuspend_usb_wq;
extern struct bus_type usb_bus_type;
+extern struct device_type usb_device_type;
+extern struct device_type usb_if_device_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;
+ return dev->type == &usb_device_type;
}
/* Do the same for device drivers and interface drivers. */
@@ -126,11 +120,11 @@ extern const char *usbcore_name;
extern struct mutex usbfs_mutex;
extern struct usb_driver usbfs_driver;
extern const struct file_operations usbfs_devices_fops;
-extern const struct file_operations usbfs_device_file_operations;
+extern const struct file_operations usbdev_file_operations;
extern void usbfs_conn_disc_event(void);
-extern int usbdev_init(void);
-extern void usbdev_cleanup(void);
+extern int usb_devio_init(void);
+extern void usb_devio_cleanup(void);
struct dev_state {
struct list_head list; /* state list */
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 4097a86c4b5e..8065f2b53701 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -68,6 +68,27 @@ choice
Many controller drivers are platform-specific; these
often need board-specific hooks.
+config USB_GADGET_FSL_USB2
+ boolean "Freescale Highspeed USB DR Peripheral Controller"
+ depends on MPC834x || PPC_MPC831x
+ select USB_GADGET_DUALSPEED
+ help
+ Some of Freescale PowerPC processors have a High Speed
+ Dual-Role(DR) USB controller, which supports device mode.
+
+ The number of programmable endpoints is different through
+ SOC revisions.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "fsl_usb2_udc" and force
+ all gadget drivers to also be dynamically linked.
+
+config USB_FSL_USB2
+ tristate
+ depends on USB_GADGET_FSL_USB2
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
config USB_GADGET_NET2280
boolean "NetChip 228x"
depends on PCI
@@ -370,6 +391,7 @@ config USB_GADGETFS
config USB_FILE_STORAGE
tristate "File-backed Storage Gadget"
+ depends on BLOCK
help
The File-backed Storage Gadget acts as a USB Mass Storage
disk drive. As its storage repository it can use a regular
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index e71e086a1cfa..5db19396631c 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
+obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
#
# USB gadget drivers
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 04e6b8508fb6..1dd8b57f4420 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -282,6 +282,9 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
+#ifdef CONFIG_USB_GADGET_FSL_USB2
+#define DEV_CONFIG_CDC
+#endif
/* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk (without notable bugs) can do this.
@@ -1735,7 +1738,8 @@ enomem:
defer_kevent (dev, WORK_RX_MEMORY);
if (retval) {
DEBUG (dev, "rx submit --> %d\n", retval);
- dev_kfree_skb_any (skb);
+ if (skb)
+ dev_kfree_skb_any(skb);
spin_lock(&dev->req_lock);
list_add (&req->list, &dev->rx_reqs);
spin_unlock(&dev->req_lock);
@@ -1766,7 +1770,6 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req)
break;
}
- skb->dev = dev->net;
skb->protocol = eth_type_trans (skb, dev->net);
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c
new file mode 100644
index 000000000000..157054ea3978
--- /dev/null
+++ b/drivers/usb/gadget/fsl_usb2_udc.c
@@ -0,0 +1,2500 @@
+/*
+ * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved.
+ *
+ * Author: Li Yang <leoli@freescale.com>
+ * Jiang Bo <tanya.jiang@freescale.com>
+ *
+ * Description:
+ * Freescale high-speed USB SOC DR module device controller driver.
+ * This can be found on MPC8349E/MPC8313E cpus.
+ * The driver is previously named as mpc_udc. Based on bare board
+ * code from Dave Liu and Shlomi Gridish.
+ *
+ * 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.
+ */
+
+#undef VERBOSE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/dmapool.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+
+#include "fsl_usb2_udc.h"
+
+#define DRIVER_DESC "Freescale High-Speed USB SOC Device Controller driver"
+#define DRIVER_AUTHOR "Li Yang/Jiang Bo"
+#define DRIVER_VERSION "Apr 20, 2007"
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl-usb2-udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+volatile static struct usb_dr_device *dr_regs = NULL;
+volatile static struct usb_sys_interface *usb_sys_regs = NULL;
+
+/* it is initialized in probe() */
+static struct fsl_udc *udc_controller = NULL;
+
+static const struct usb_endpoint_descriptor
+fsl_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD,
+};
+
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state);
+static int fsl_udc_resume(struct platform_device *pdev);
+static void fsl_ep_fifo_flush(struct usb_ep *_ep);
+
+#ifdef CONFIG_PPC32
+#define fsl_readl(addr) in_le32(addr)
+#define fsl_writel(addr, val32) out_le32(val32, addr)
+#else
+#define fsl_readl(addr) readl(addr)
+#define fsl_writel(addr, val32) writel(addr, val32)
+#endif
+
+/********************************************************************
+ * Internal Used Function
+********************************************************************/
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ * request is still in progress.
+ *--------------------------------------------------------------*/
+static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
+{
+ struct fsl_udc *udc = NULL;
+ unsigned char stopped = ep->stopped;
+ struct ep_td_struct *curr_td, *next_td;
+ int j;
+
+ udc = (struct fsl_udc *)ep->udc;
+ /* Removed the req from fsl_ep->queue */
+ list_del_init(&req->queue);
+
+ /* req.status should be set as -EINPROGRESS in ep_queue() */
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ /* Free dtd for the request */
+ next_td = req->head;
+ for (j = 0; j < req->dtd_count; j++) {
+ curr_td = next_td;
+ if (j != req->dtd_count - 1) {
+ next_td = curr_td->next_td_virt;
+ }
+ dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
+ }
+
+ if (req->mapped) {
+ dma_unmap_single(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else
+ dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+
+ if (status && (status != -ESHUTDOWN))
+ VDBG("complete %s req %p stat %d len %u/%u",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ ep->stopped = 1;
+
+ spin_unlock(&ep->udc->lock);
+ /* complete() is from gadget layer,
+ * eg fsg->bulk_in_complete() */
+ if (req->req.complete)
+ req->req.complete(&ep->ep, &req->req);
+
+ spin_lock(&ep->udc->lock);
+ ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ * called with spinlock held
+ *--------------------------------------------------------------*/
+static void nuke(struct fsl_ep *ep, int status)
+{
+ ep->stopped = 1;
+
+ /* Flush fifo */
+ fsl_ep_fifo_flush(&ep->ep);
+
+ /* Whether this eq has request linked */
+ while (!list_empty(&ep->queue)) {
+ struct fsl_req *req = NULL;
+
+ req = list_entry(ep->queue.next, struct fsl_req, queue);
+ done(ep, req, status);
+ }
+}
+
+/*------------------------------------------------------------------
+ Internal Hardware related function
+ ------------------------------------------------------------------*/
+
+static int dr_controller_setup(struct fsl_udc *udc)
+{
+ unsigned int tmp = 0, portctrl = 0, ctrl = 0;
+ unsigned long timeout;
+#define FSL_UDC_RESET_TIMEOUT 1000
+
+ /* before here, make sure dr_regs has been initialized */
+ if (!udc)
+ return -EINVAL;
+
+ /* Stop and reset the usb controller */
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp &= ~USB_CMD_RUN_STOP;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp |= USB_CMD_CTRL_RESET;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ /* Wait for reset to complete */
+ timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+ while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
+ if (time_after(jiffies, timeout)) {
+ ERR("udc reset timeout! \n");
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+
+ /* Set the controller as device mode */
+ tmp = fsl_readl(&dr_regs->usbmode);
+ tmp |= USB_MODE_CTRL_MODE_DEVICE;
+ /* Disable Setup Lockout */
+ tmp |= USB_MODE_SETUP_LOCK_OFF;
+ fsl_writel(tmp, &dr_regs->usbmode);
+
+ /* Clear the setup status */
+ fsl_writel(0, &dr_regs->usbsts);
+
+ tmp = udc->ep_qh_dma;
+ tmp &= USB_EP_LIST_ADDRESS_MASK;
+ fsl_writel(tmp, &dr_regs->endpointlistaddr);
+
+ VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",
+ (int)udc->ep_qh, (int)tmp,
+ fsl_readl(&dr_regs->endpointlistaddr));
+
+ /* Config PHY interface */
+ portctrl = fsl_readl(&dr_regs->portsc1);
+ portctrl &= ~PORTSCX_PHY_TYPE_SEL;
+ switch (udc->phy_mode) {
+ case FSL_USB2_PHY_ULPI:
+ portctrl |= PORTSCX_PTS_ULPI;
+ break;
+ case FSL_USB2_PHY_UTMI:
+ case FSL_USB2_PHY_UTMI_WIDE:
+ portctrl |= PORTSCX_PTS_UTMI;
+ break;
+ case FSL_USB2_PHY_SERIAL:
+ portctrl |= PORTSCX_PTS_FSLS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ fsl_writel(portctrl, &dr_regs->portsc1);
+
+ /* Config control enable i/o output, cpu endian register */
+ ctrl = __raw_readl(&usb_sys_regs->control);
+ ctrl |= USB_CTRL_IOENB;
+ __raw_writel(ctrl, &usb_sys_regs->control);
+
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+ /* Turn on cache snooping hardware, since some PowerPC platforms
+ * wholly rely on hardware to deal with cache coherent. */
+
+ /* Setup Snooping for all the 4GB space */
+ tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */
+ __raw_writel(tmp, &usb_sys_regs->snoop1);
+ tmp |= 0x80000000; /* starts from 0x8000000, size 2G */
+ __raw_writel(tmp, &usb_sys_regs->snoop2);
+#endif
+
+ return 0;
+}
+
+/* Enable DR irq and set controller to run state */
+static void dr_controller_run(struct fsl_udc *udc)
+{
+ u32 temp;
+
+ /* Enable DR irq reg */
+ temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
+ | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN
+ | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;
+
+ fsl_writel(temp, &dr_regs->usbintr);
+
+ /* Clear stopped bit */
+ udc->stopped = 0;
+
+ /* Set the controller as device mode */
+ temp = fsl_readl(&dr_regs->usbmode);
+ temp |= USB_MODE_CTRL_MODE_DEVICE;
+ fsl_writel(temp, &dr_regs->usbmode);
+
+ /* Set controller to Run */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ temp |= USB_CMD_RUN_STOP;
+ fsl_writel(temp, &dr_regs->usbcmd);
+
+ return;
+}
+
+static void dr_controller_stop(struct fsl_udc *udc)
+{
+ unsigned int tmp;
+
+ /* disable all INTR */
+ fsl_writel(0, &dr_regs->usbintr);
+
+ /* Set stopped bit for isr */
+ udc->stopped = 1;
+
+ /* disable IO output */
+/* usb_sys_regs->control = 0; */
+
+ /* set controller to Stop */
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp &= ~USB_CMD_RUN_STOP;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ return;
+}
+
+void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type)
+{
+ unsigned int tmp_epctrl = 0;
+
+ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (dir) {
+ if (ep_num)
+ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ tmp_epctrl |= EPCTRL_TX_ENABLE;
+ tmp_epctrl |= ((unsigned int)(ep_type)
+ << EPCTRL_TX_EP_TYPE_SHIFT);
+ } else {
+ if (ep_num)
+ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+ tmp_epctrl |= EPCTRL_RX_ENABLE;
+ tmp_epctrl |= ((unsigned int)(ep_type)
+ << EPCTRL_RX_EP_TYPE_SHIFT);
+ }
+
+ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+static void
+dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value)
+{
+ u32 tmp_epctrl = 0;
+
+ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+
+ if (value) {
+ /* set the stall bit */
+ if (dir)
+ tmp_epctrl |= EPCTRL_TX_EP_STALL;
+ else
+ tmp_epctrl |= EPCTRL_RX_EP_STALL;
+ } else {
+ /* clear the stall bit and reset data toggle */
+ if (dir) {
+ tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
+ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ } else {
+ tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
+ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+ }
+ }
+ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+/* Get stall status of a specific ep
+ Return: 0: not stalled; 1:stalled */
+static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir)
+{
+ u32 epctrl;
+
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (dir)
+ return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0;
+ else
+ return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0;
+}
+
+/********************************************************************
+ Internal Structure Build up functions
+********************************************************************/
+
+/*------------------------------------------------------------------
+* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH
+ * @zlt: Zero Length Termination Select (1: disable; 0: enable)
+ * @mult: Mult field
+ ------------------------------------------------------------------*/
+static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
+ unsigned char dir, unsigned char ep_type,
+ unsigned int max_pkt_len,
+ unsigned int zlt, unsigned char mult)
+{
+ struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir];
+ unsigned int tmp = 0;
+
+ /* set the Endpoint Capabilites in QH */
+ switch (ep_type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* Interrupt On Setup (IOS). for control ep */
+ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+ | EP_QUEUE_HEAD_IOS;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+ | (mult << EP_QUEUE_HEAD_MULT_POS);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
+ break;
+ default:
+ VDBG("error ep type is %d", ep_type);
+ return;
+ }
+ if (zlt)
+ tmp |= EP_QUEUE_HEAD_ZLT_SEL;
+ p_QH->max_pkt_length = cpu_to_le32(tmp);
+
+ return;
+}
+
+/* Setup qh structure and ep register for ep0. */
+static void ep0_setup(struct fsl_udc *udc)
+{
+ /* the intialization of an ep includes: fields in QH, Regs,
+ * fsl_ep struct */
+ struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
+ USB_MAX_CTRL_PAYLOAD, 0, 0);
+ struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,
+ USB_MAX_CTRL_PAYLOAD, 0, 0);
+ dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);
+ dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);
+
+ return;
+
+}
+
+/***********************************************************************
+ Endpoint Management Functions
+***********************************************************************/
+
+/*-------------------------------------------------------------------------
+ * when configurations are set, or when interface settings change
+ * for example the do_set_interface() in gadget layer,
+ * the driver will enable or disable the relevant endpoints
+ * ep0 doesn't use this routine. It is always enabled.
+-------------------------------------------------------------------------*/
+static int fsl_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct fsl_udc *udc = NULL;
+ struct fsl_ep *ep = NULL;
+ unsigned short max = 0;
+ unsigned char mult = 0, zlt;
+ int retval = -EINVAL;
+ unsigned long flags = 0;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+
+ /* catch various bogus parameters */
+ if (!_ep || !desc || ep->desc
+ || (desc->bDescriptorType != USB_DT_ENDPOINT))
+ return -EINVAL;
+
+ udc = ep->udc;
+
+ if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+ return -ESHUTDOWN;
+
+ max = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* Disable automatic zlp generation. Driver is reponsible to indicate
+ * explicitly through req->req.zero. This is needed to enable multi-td
+ * request. */
+ zlt = 1;
+
+ /* Assume the max packet size from gadget is always correct */
+ switch (desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ /* mult = 0. Execute N Transactions as demonstrated by
+ * the USB variable length packet protocol where N is
+ * computed using the Maximum Packet Length (dQH) and
+ * the Total Bytes field (dTD) */
+ mult = 0;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* Calculate transactions needed for high bandwidth iso */
+ mult = (unsigned char)(1 + ((max >> 11) & 0x03));
+ max = max & 0x8ff; /* bit 0~10 */
+ /* 3 transactions at most */
+ if (mult > 3)
+ goto en_done;
+ break;
+ default:
+ goto en_done;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+ ep->ep.maxpacket = max;
+ ep->desc = desc;
+ ep->stopped = 0;
+
+ /* Controller related setup */
+ /* Init EPx Queue Head (Ep Capabilites field in QH
+ * according to max, zlt, mult) */
+ struct_ep_qh_setup(udc, (unsigned char) ep_index(ep),
+ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+ ? USB_SEND : USB_RECV),
+ (unsigned char) (desc->bmAttributes
+ & USB_ENDPOINT_XFERTYPE_MASK),
+ max, zlt, mult);
+
+ /* Init endpoint ctrl register */
+ dr_ep_setup((unsigned char) ep_index(ep),
+ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+ ? USB_SEND : USB_RECV),
+ (unsigned char) (desc->bmAttributes
+ & USB_ENDPOINT_XFERTYPE_MASK));
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ retval = 0;
+
+ VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name,
+ ep->desc->bEndpointAddress & 0x0f,
+ (desc->bEndpointAddress & USB_DIR_IN)
+ ? "in" : "out", max);
+en_done:
+ return retval;
+}
+
+/*---------------------------------------------------------------------
+ * @ep : the ep being unconfigured. May not be ep0
+ * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
+*---------------------------------------------------------------------*/
+static int fsl_ep_disable(struct usb_ep *_ep)
+{
+ struct fsl_udc *udc = NULL;
+ struct fsl_ep *ep = NULL;
+ unsigned long flags = 0;
+ u32 epctrl;
+ int ep_num;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+ if (!_ep || !ep->desc) {
+ VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+
+ /* disable ep on controller */
+ ep_num = ep_index(ep);
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl &= ~EPCTRL_TX_ENABLE;
+ else
+ epctrl &= ~EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+ udc = (struct fsl_udc *)ep->udc;
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* nuke all pending requests (does flush) */
+ nuke(ep, -ESHUTDOWN);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ VDBG("disabled %s OK", _ep->name);
+ return 0;
+}
+
+/*---------------------------------------------------------------------
+ * allocate a request object used by this endpoint
+ * the main operation is to insert the req->queue to the eq->queue
+ * Returns the request, or null if one could not be allocated
+*---------------------------------------------------------------------*/
+static struct usb_request *
+fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct fsl_req *req = NULL;
+
+ req = kzalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return NULL;
+
+ req->req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fsl_req *req = NULL;
+
+ req = container_of(_req, struct fsl_req, req);
+
+ if (_req)
+ kfree(req);
+}
+
+/*------------------------------------------------------------------
+ * Allocate an I/O buffer
+*---------------------------------------------------------------------*/
+static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+ dma_addr_t *dma, gfp_t gfp_flags)
+{
+ struct fsl_ep *ep;
+
+ if (!_ep)
+ return NULL;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+
+ return dma_alloc_coherent(ep->udc->gadget.dev.parent,
+ bytes, dma, gfp_flags);
+}
+
+/*------------------------------------------------------------------
+ * frees an i/o buffer
+*---------------------------------------------------------------------*/
+static void fsl_free_buffer(struct usb_ep *_ep, void *buf,
+ dma_addr_t dma, unsigned bytes)
+{
+ struct fsl_ep *ep;
+
+ if (!_ep)
+ return NULL;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+
+ dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma);
+}
+
+/*-------------------------------------------------------------------------*/
+static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
+{
+ int i = ep_index(ep) * 2 + ep_is_in(ep);
+ u32 temp, bitmask, tmp_stat;
+ struct ep_queue_head *dQH = &ep->udc->ep_qh[i];
+
+ /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr);
+ VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */
+
+ bitmask = ep_is_in(ep)
+ ? (1 << (ep_index(ep) + 16))
+ : (1 << (ep_index(ep)));
+
+ /* check if the pipe is empty */
+ if (!(list_empty(&ep->queue))) {
+ /* Add td to the end */
+ struct fsl_req *lastreq;
+ lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
+ lastreq->tail->next_td_ptr =
+ cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK);
+ /* Read prime bit, if 1 goto done */
+ if (fsl_readl(&dr_regs->endpointprime) & bitmask)
+ goto out;
+
+ do {
+ /* Set ATDTW bit in USBCMD */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+ /* Read correct status bit */
+ tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask;
+
+ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW));
+
+ /* Write ATDTW bit to 0 */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+ if (tmp_stat)
+ goto out;
+ }
+
+ /* Write dQH next pointer and terminate bit to 0 */
+ temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+ dQH->next_dtd_ptr = cpu_to_le32(temp);
+
+ /* Clear active and halt bit */
+ temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
+ | EP_QUEUE_HEAD_STATUS_HALT));
+ dQH->size_ioc_int_sts &= temp;
+
+ /* Prime endpoint by writing 1 to ENDPTPRIME */
+ temp = ep_is_in(ep)
+ ? (1 << (ep_index(ep) + 16))
+ : (1 << (ep_index(ep)));
+ fsl_writel(temp, &dr_regs->endpointprime);
+out:
+ return 0;
+}
+
+/* Fill in the dTD structure
+ * @req: request that the transfer belongs to
+ * @length: return actually data length of the dTD
+ * @dma: return dma address of the dTD
+ * @is_last: return flag if it is the last dTD of the request
+ * return: pointer to the built dTD */
+static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
+ dma_addr_t *dma, int *is_last)
+{
+ u32 swap_temp;
+ struct ep_td_struct *dtd;
+
+ /* how big will this transfer be? */
+ *length = min(req->req.length - req->req.actual,
+ (unsigned)EP_MAX_LENGTH_TRANSFER);
+
+ dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma);
+ if (dtd == NULL)
+ return dtd;
+
+ dtd->td_dma = *dma;
+ /* Clear reserved field */
+ swap_temp = cpu_to_le32(dtd->size_ioc_sts);
+ swap_temp &= ~DTD_RESERVED_FIELDS;
+ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+
+ /* Init all of buffer page pointers */
+ swap_temp = (u32) (req->req.dma + req->req.actual);
+ dtd->buff_ptr0 = cpu_to_le32(swap_temp);
+ dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000);
+ dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000);
+ dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000);
+ dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000);
+
+ req->req.actual += *length;
+
+ /* zlp is needed if req->req.zero is set */
+ if (req->req.zero) {
+ if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+ *is_last = 1;
+ else
+ *is_last = 0;
+ } else if (req->req.length == req->req.actual)
+ *is_last = 1;
+ else
+ *is_last = 0;
+
+ if ((*is_last) == 0)
+ VDBG("multi-dtd request!\n");
+ /* Fill in the transfer size; set active bit */
+ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+
+ /* Enable interrupt for the last dtd of a request */
+ if (*is_last && !req->req.no_interrupt)
+ swap_temp |= DTD_IOC;
+
+ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+
+ mb();
+
+ VDBG("length = %d address= 0x%x", *length, (int)*dma);
+
+ return dtd;
+}
+
+/* Generate dtd chain for a request */
+static int fsl_req_to_dtd(struct fsl_req *req)
+{
+ unsigned count;
+ int is_last;
+ int is_first =1;
+ struct ep_td_struct *last_dtd = NULL, *dtd;
+ dma_addr_t dma;
+
+ do {
+ dtd = fsl_build_dtd(req, &count, &dma, &is_last);
+ if (dtd == NULL)
+ return -ENOMEM;
+
+ if (is_first) {
+ is_first = 0;
+ req->head = dtd;
+ } else {
+ last_dtd->next_td_ptr = cpu_to_le32(dma);
+ last_dtd->next_td_virt = dtd;
+ }
+ last_dtd = dtd;
+
+ req->dtd_count++;
+ } while (!is_last);
+
+ dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
+
+ req->tail = dtd;
+
+ return 0;
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+ struct fsl_req *req = container_of(_req, struct fsl_req, req);
+ struct fsl_udc *udc;
+ unsigned long flags;
+ int is_iso = 0;
+
+ /* catch various bogus parameters */
+ if (!_req || !req->req.complete || !req->req.buf
+ || !list_empty(&req->queue)) {
+ VDBG("%s, bad params\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (!_ep || (!ep->desc && ep_index(ep))) {
+ VDBG("%s, bad ep\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ if (req->req.length > ep->ep.maxpacket)
+ return -EMSGSIZE;
+ is_iso = 1;
+ }
+
+ udc = ep->udc;
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ req->ep = ep;
+
+ /* map virtual address to hardware */
+ if (req->req.dma == DMA_ADDR_INVALID) {
+ req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+ req->req.buf,
+ req->req.length, ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 1;
+ } else {
+ dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 0;
+ }
+
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->dtd_count = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* build dtds and push them to device queue */
+ if (!fsl_req_to_dtd(req)) {
+ fsl_queue_td(ep, req);
+ } else {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -ENOMEM;
+ }
+
+ /* Update ep0 state */
+ if ((ep_index(ep) == 0))
+ udc->ep0_state = DATA_STATE_XMIT;
+
+ /* irq handler advances the queue */
+ if (req != NULL)
+ list_add_tail(&req->queue, &ep->queue);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+ struct fsl_req *req;
+ unsigned long flags;
+ int ep_num, stopped, ret = 0;
+ u32 epctrl;
+
+ if (!_ep || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ stopped = ep->stopped;
+
+ /* Stop the ep before we deal with the queue */
+ ep->stopped = 1;
+ ep_num = ep_index(ep);
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl &= ~EPCTRL_TX_ENABLE;
+ else
+ epctrl &= ~EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* The request is in progress, or completed but not dequeued */
+ if (ep->queue.next == &req->queue) {
+ _req->status = -ECONNRESET;
+ fsl_ep_fifo_flush(_ep); /* flush current transfer */
+
+ /* The request isn't the last request in this ep queue */
+ if (req->queue.next != &ep->queue) {
+ struct ep_queue_head *qh;
+ struct fsl_req *next_req;
+
+ qh = ep->qh;
+ next_req = list_entry(req->queue.next, struct fsl_req,
+ queue);
+
+ /* Point the QH to the first TD of next request */
+ fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr);
+ }
+
+ /* The request hasn't been processed, patch up the TD chain */
+ } else {
+ struct fsl_req *prev_req;
+
+ prev_req = list_entry(req->queue.prev, struct fsl_req, queue);
+ fsl_writel(fsl_readl(&req->tail->next_td_ptr),
+ &prev_req->tail->next_td_ptr);
+
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ /* Enable EP */
+out: epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl |= EPCTRL_TX_ENABLE;
+ else
+ epctrl |= EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+ ep->stopped = stopped;
+
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt 0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct fsl_ep *ep = NULL;
+ unsigned long flags = 0;
+ int status = -EOPNOTSUPP; /* operation not supported */
+ unsigned char ep_dir = 0, ep_num = 0;
+ struct fsl_udc *udc = NULL;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+ udc = ep->udc;
+ if (!_ep || !ep->desc) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ status = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Attempt to halt IN ep will fail if any transfer requests
+ * are still queue */
+ if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+ status = -EAGAIN;
+ goto out;
+ }
+
+ status = 0;
+ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+ ep_num = (unsigned char)(ep_index(ep));
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ dr_ep_change_stall(ep_num, ep_dir, value);
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+ if (ep_index(ep) == 0) {
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = 0;
+ }
+out:
+ VDBG(" %s %s halt stat %d", ep->ep.name,
+ value ? "set" : "clear", status);
+
+ return status;
+}
+
+static void fsl_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct fsl_ep *ep;
+ int ep_num, ep_dir;
+ u32 bits;
+ unsigned long timeout;
+#define FSL_UDC_FLUSH_TIMEOUT 1000
+
+ if (!_ep) {
+ return;
+ } else {
+ ep = container_of(_ep, struct fsl_ep, ep);
+ if (!ep->desc)
+ return;
+ }
+ ep_num = ep_index(ep);
+ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+
+ if (ep_num == 0)
+ bits = (1 << 16) | 1;
+ else if (ep_dir == USB_SEND)
+ bits = 1 << (16 + ep_num);
+ else
+ bits = 1 << ep_num;
+
+ timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT;
+ do {
+ fsl_writel(bits, &dr_regs->endptflush);
+
+ /* Wait until flush complete */
+ while (fsl_readl(&dr_regs->endptflush)) {
+ if (time_after(jiffies, timeout)) {
+ ERR("ep flush timeout\n");
+ return;
+ }
+ cpu_relax();
+ }
+ /* See if we need to flush again */
+ } while (fsl_readl(&dr_regs->endptstatus) & bits);
+}
+
+static struct usb_ep_ops fsl_ep_ops = {
+ .enable = fsl_ep_enable,
+ .disable = fsl_ep_disable,
+
+ .alloc_request = fsl_alloc_request,
+ .free_request = fsl_free_request,
+
+ .alloc_buffer = fsl_alloc_buffer,
+ .free_buffer = fsl_free_buffer,
+
+ .queue = fsl_ep_queue,
+ .dequeue = fsl_ep_dequeue,
+
+ .set_halt = fsl_ep_set_halt,
+ .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */
+};
+
+/*-------------------------------------------------------------------------
+ Gadget Driver Layer Operations
+-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------
+ * Get the current frame number (from DR frame_index Reg )
+ *----------------------------------------------------------------------*/
+static int fsl_get_frame(struct usb_gadget *gadget)
+{
+ return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);
+}
+
+/*-----------------------------------------------------------------------
+ * Tries to wake up the host connected to this gadget
+ -----------------------------------------------------------------------*/
+static int fsl_wakeup(struct usb_gadget *gadget)
+{
+ struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget);
+ u32 portsc;
+
+ /* Remote wakeup feature not enabled by host */
+ if (!udc->remote_wakeup)
+ return -ENOTSUPP;
+
+ portsc = fsl_readl(&dr_regs->portsc1);
+ /* not suspended? */
+ if (!(portsc & PORTSCX_PORT_SUSPEND))
+ return 0;
+ /* trigger force resume */
+ portsc |= PORTSCX_PORT_FORCE_RESUME;
+ fsl_writel(portsc, &dr_regs->portsc1);
+ return 0;
+}
+
+static int can_pullup(struct fsl_udc *udc)
+{
+ return udc->driver && udc->softconnect && udc->vbus_active;
+}
+
+/* Notify controller that VBUS is powered, Called by whatever
+ detects VBUS sessions */
+static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct fsl_udc *udc;
+ unsigned long flags;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+ VDBG("VBUS %s\n", is_active ? "on" : "off");
+ udc->vbus_active = (is_active != 0);
+ if (can_pullup(udc))
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ else
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume. For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+#ifdef CONFIG_USB_OTG
+ struct fsl_udc *udc;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+
+ if (udc->transceiver)
+ return otg_set_power(udc->transceiver, mA);
+#endif
+ return -ENOTSUPP;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int fsl_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct fsl_udc *udc;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+ udc->softconnect = (is_on != 0);
+ if (can_pullup(udc))
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ else
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+
+ return 0;
+}
+
+/* defined in usb_gadget.h */
+static struct usb_gadget_ops fsl_gadget_ops = {
+ .get_frame = fsl_get_frame,
+ .wakeup = fsl_wakeup,
+/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */
+ .vbus_session = fsl_vbus_session,
+ .vbus_draw = fsl_vbus_draw,
+ .pullup = fsl_pullup,
+};
+
+/* Set protocol stall on ep0, protocol stall will automatically be cleared
+ on new transaction */
+static void ep0stall(struct fsl_udc *udc)
+{
+ u32 tmp;
+
+ /* must set tx and rx to stall at the same time */
+ tmp = fsl_readl(&dr_regs->endptctrl[0]);
+ tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
+ fsl_writel(tmp, &dr_regs->endptctrl[0]);
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = 0;
+}
+
+/* Prime a status phase for ep0 */
+static int ep0_prime_status(struct fsl_udc *udc, int direction)
+{
+ struct fsl_req *req = udc->status_req;
+ struct fsl_ep *ep;
+ int status = 0;
+
+ if (direction == EP_DIR_IN)
+ udc->ep0_dir = USB_DIR_IN;
+ else
+ udc->ep0_dir = USB_DIR_OUT;
+
+ ep = &udc->eps[0];
+ udc->ep0_state = WAIT_FOR_OUT_STATUS;
+
+ req->ep = ep;
+ req->req.length = 0;
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->req.complete = NULL;
+ req->dtd_count = 0;
+
+ if (fsl_req_to_dtd(req) == 0)
+ status = fsl_queue_td(ep, req);
+ else
+ return -ENOMEM;
+
+ if (status)
+ ERR("Can't queue ep0 status request \n");
+ list_add_tail(&req->queue, &ep->queue);
+
+ return status;
+}
+
+static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)
+{
+ struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
+
+ if (!ep->name)
+ return 0;
+
+ nuke(ep, -ESHUTDOWN);
+
+ return 0;
+}
+
+/*
+ * ch9 Set address
+ */
+static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length)
+{
+ /* Save the new address to device struct */
+ udc->device_address = (u8) value;
+ /* Update usb state */
+ udc->usb_state = USB_STATE_ADDRESS;
+ /* Status phase */
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ ep0stall(udc);
+}
+
+/*
+ * ch9 Get status
+ */
+static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
+ u16 index, u16 length)
+{
+ u16 tmp = 0; /* Status, cpu endian */
+
+ struct fsl_req *req;
+ struct fsl_ep *ep;
+ int status = 0;
+
+ ep = &udc->eps[0];
+
+ if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+ /* Get device status */
+ tmp = 1 << USB_DEVICE_SELF_POWERED;
+ tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+ /* Get interface status */
+ /* We don't have interface information in udc driver */
+ tmp = 0;
+ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+ /* Get endpoint status */
+ struct fsl_ep *target_ep;
+
+ target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index));
+
+ /* stall if endpoint doesn't exist */
+ if (!target_ep->desc)
+ goto stall;
+ tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
+ << USB_ENDPOINT_HALT;
+ }
+
+ udc->ep0_dir = USB_DIR_IN;
+ /* Borrow the per device status_req */
+ req = udc->status_req;
+ /* Fill in the reqest structure */
+ *((u16 *) req->req.buf) = cpu_to_le16(tmp);
+ req->ep = ep;
+ req->req.length = 2;
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->req.complete = NULL;
+ req->dtd_count = 0;
+
+ /* prime the data phase */
+ if ((fsl_req_to_dtd(req) == 0))
+ status = fsl_queue_td(ep, req);
+ else /* no mem */
+ goto stall;
+
+ if (status) {
+ ERR("Can't respond to getstatus request \n");
+ goto stall;
+ }
+ list_add_tail(&req->queue, &ep->queue);
+ udc->ep0_state = DATA_STATE_XMIT;
+ return;
+stall:
+ ep0stall(udc);
+}
+
+static void setup_received_irq(struct fsl_udc *udc,
+ struct usb_ctrlrequest *setup)
+{
+ u16 wValue = le16_to_cpu(setup->wValue);
+ u16 wIndex = le16_to_cpu(setup->wIndex);
+ u16 wLength = le16_to_cpu(setup->wLength);
+
+ udc_reset_ep_queue(udc, 0);
+
+ switch (setup->bRequest) {
+ /* Request that need Data+Status phase from udc */
+ case USB_REQ_GET_STATUS:
+ if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_STANDARD))
+ != (USB_DIR_IN | USB_TYPE_STANDARD))
+ break;
+ ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength);
+ break;
+
+ /* Requests that need Status phase from udc */
+ case USB_REQ_SET_ADDRESS:
+ if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
+ | USB_RECIP_DEVICE))
+ break;
+ ch9setaddress(udc, wValue, wIndex, wLength);
+ break;
+
+ /* Handled by udc, no data, status by udc */
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ { /* status transaction */
+ int rc = -EOPNOTSUPP;
+
+ if ((setup->bRequestType & USB_RECIP_MASK)
+ == USB_RECIP_ENDPOINT) {
+ int pipe = get_pipe_by_windex(wIndex);
+ struct fsl_ep *ep;
+
+ if (wValue != 0 || wLength != 0 || pipe > udc->max_ep)
+ break;
+ ep = get_ep_by_pipe(udc, pipe);
+
+ spin_unlock(&udc->lock);
+ rc = fsl_ep_set_halt(&ep->ep,
+ (setup->bRequest == USB_REQ_SET_FEATURE)
+ ? 1 : 0);
+ spin_lock(&udc->lock);
+
+ } else if ((setup->bRequestType & USB_RECIP_MASK)
+ == USB_RECIP_DEVICE) {
+ /* Note: The driver has not include OTG support yet.
+ * This will be set when OTG support is added */
+ if (!udc->gadget.is_otg)
+ break;
+ else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE)
+ udc->gadget.b_hnp_enable = 1;
+ else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
+ udc->gadget.a_hnp_support = 1;
+ else if (setup->bRequest ==
+ USB_DEVICE_A_ALT_HNP_SUPPORT)
+ udc->gadget.a_alt_hnp_support = 1;
+ rc = 0;
+ }
+ if (rc == 0) {
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ ep0stall(udc);
+ }
+ break;
+ }
+ /* Requests handled by gadget */
+ default:
+ if (wLength) {
+ /* Data phase from gadget, status phase from udc */
+ udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+ ? USB_DIR_IN : USB_DIR_OUT;
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) < 0)
+ ep0stall(udc);
+ spin_lock(&udc->lock);
+ udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
+ ? DATA_STATE_XMIT : DATA_STATE_RECV;
+
+ } else {
+ /* No data phase, IN status from gadget */
+ udc->ep0_dir = USB_DIR_IN;
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) < 0)
+ ep0stall(udc);
+ spin_lock(&udc->lock);
+ udc->ep0_state = WAIT_FOR_OUT_STATUS;
+ }
+ break;
+ }
+}
+
+/* Process request for Data or Status phase of ep0
+ * prime status phase if needed */
+static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
+ struct fsl_req *req)
+{
+ if (udc->usb_state == USB_STATE_ADDRESS) {
+ /* Set the new address */
+ u32 new_address = (u32) udc->device_address;
+ fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS,
+ &dr_regs->deviceaddr);
+ }
+
+ done(ep0, req, 0);
+
+ switch (udc->ep0_state) {
+ case DATA_STATE_XMIT:
+ /* receive status phase */
+ if (ep0_prime_status(udc, EP_DIR_OUT))
+ ep0stall(udc);
+ break;
+ case DATA_STATE_RECV:
+ /* send status phase */
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ ep0stall(udc);
+ break;
+ case WAIT_FOR_OUT_STATUS:
+ udc->ep0_state = WAIT_FOR_SETUP;
+ break;
+ case WAIT_FOR_SETUP:
+ ERR("Unexpect ep0 packets \n");
+ break;
+ default:
+ ep0stall(udc);
+ break;
+ }
+}
+
+/* Tripwire mechanism to ensure a setup packet payload is extracted without
+ * being corrupted by another incoming setup packet */
+static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
+{
+ u32 temp;
+ struct ep_queue_head *qh;
+
+ qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
+
+ /* Clear bit in ENDPTSETUPSTAT */
+ temp = fsl_readl(&dr_regs->endptsetupstat);
+ fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat);
+
+ /* while a hazard exists when setup package arrives */
+ do {
+ /* Set Setup Tripwire */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd);
+
+ /* Copy the setup packet to local buffer */
+ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW));
+
+ /* Clear Setup Tripwire */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd);
+}
+
+/* process-ep_req(): free the completed Tds for this req */
+static int process_ep_req(struct fsl_udc *udc, int pipe,
+ struct fsl_req *curr_req)
+{
+ struct ep_td_struct *curr_td;
+ int td_complete, actual, remaining_length, j, tmp;
+ int status = 0;
+ int errors = 0;
+ struct ep_queue_head *curr_qh = &udc->ep_qh[pipe];
+ int direction = pipe % 2;
+
+ curr_td = curr_req->head;
+ td_complete = 0;
+ actual = curr_req->req.length;
+
+ for (j = 0; j < curr_req->dtd_count; j++) {
+ remaining_length = (le32_to_cpu(curr_td->size_ioc_sts)
+ & DTD_PACKET_SIZE)
+ >> DTD_LENGTH_BIT_POS;
+ actual -= remaining_length;
+
+ if ((errors = le32_to_cpu(curr_td->size_ioc_sts) &
+ DTD_ERROR_MASK)) {
+ if (errors & DTD_STATUS_HALTED) {
+ ERR("dTD error %08x QH=%d\n", errors, pipe);
+ /* Clear the errors and Halt condition */
+ tmp = le32_to_cpu(curr_qh->size_ioc_int_sts);
+ tmp &= ~errors;
+ curr_qh->size_ioc_int_sts = cpu_to_le32(tmp);
+ status = -EPIPE;
+ /* FIXME: continue with next queued TD? */
+
+ break;
+ }
+ if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+ VDBG("Transfer overflow");
+ status = -EPROTO;
+ break;
+ } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+ VDBG("ISO error");
+ status = -EILSEQ;
+ break;
+ } else
+ ERR("Unknown error has occured (0x%x)!\r\n",
+ errors);
+
+ } else if (le32_to_cpu(curr_td->size_ioc_sts)
+ & DTD_STATUS_ACTIVE) {
+ VDBG("Request not complete");
+ status = REQ_UNCOMPLETE;
+ return status;
+ } else if (remaining_length) {
+ if (direction) {
+ VDBG("Transmit dTD remaining length not zero");
+ status = -EPROTO;
+ break;
+ } else {
+ td_complete++;
+ break;
+ }
+ } else {
+ td_complete++;
+ VDBG("dTD transmitted successful ");
+ }
+
+ if (j != curr_req->dtd_count - 1)
+ curr_td = (struct ep_td_struct *)curr_td->next_td_virt;
+ }
+
+ if (status)
+ return status;
+
+ curr_req->req.actual = actual;
+
+ return 0;
+}
+
+/* Process a DTD completion interrupt */
+static void dtd_complete_irq(struct fsl_udc *udc)
+{
+ u32 bit_pos;
+ int i, ep_num, direction, bit_mask, status;
+ struct fsl_ep *curr_ep;
+ struct fsl_req *curr_req, *temp_req;
+
+ /* Clear the bits in the register */
+ bit_pos = fsl_readl(&dr_regs->endptcomplete);
+ fsl_writel(bit_pos, &dr_regs->endptcomplete);
+
+ if (!bit_pos)
+ return;
+
+ for (i = 0; i < udc->max_ep * 2; i++) {
+ ep_num = i >> 1;
+ direction = i % 2;
+
+ bit_mask = 1 << (ep_num + 16 * direction);
+
+ if (!(bit_pos & bit_mask))
+ continue;
+
+ curr_ep = get_ep_by_pipe(udc, i);
+
+ /* If the ep is configured */
+ if (curr_ep->name == NULL) {
+ WARN("Invalid EP?");
+ continue;
+ }
+
+ /* process the req queue until an uncomplete request */
+ list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue,
+ queue) {
+ status = process_ep_req(udc, i, curr_req);
+
+ VDBG("status of process_ep_req= %d, ep = %d",
+ status, ep_num);
+ if (status == REQ_UNCOMPLETE)
+ break;
+ /* write back status to req */
+ curr_req->req.status = status;
+
+ if (ep_num == 0) {
+ ep0_req_complete(udc, curr_ep, curr_req);
+ break;
+ } else
+ done(curr_ep, curr_req, status);
+ }
+ }
+}
+
+/* Process a port change interrupt */
+static void port_change_irq(struct fsl_udc *udc)
+{
+ u32 speed;
+
+ if (udc->bus_reset)
+ udc->bus_reset = 0;
+
+ /* Bus resetting is finished */
+ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
+ /* Get the speed */
+ speed = (fsl_readl(&dr_regs->portsc1)
+ & PORTSCX_PORT_SPEED_MASK);
+ switch (speed) {
+ case PORTSCX_PORT_SPEED_HIGH:
+ udc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case PORTSCX_PORT_SPEED_FULL:
+ udc->gadget.speed = USB_SPEED_FULL;
+ break;
+ case PORTSCX_PORT_SPEED_LOW:
+ udc->gadget.speed = USB_SPEED_LOW;
+ break;
+ default:
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ /* Update USB state */
+ if (!udc->resume_state)
+ udc->usb_state = USB_STATE_DEFAULT;
+}
+
+/* Process suspend interrupt */
+static void suspend_irq(struct fsl_udc *udc)
+{
+ udc->resume_state = udc->usb_state;
+ udc->usb_state = USB_STATE_SUSPENDED;
+
+ /* report suspend to the driver, serial.c does not support this */
+ if (udc->driver->suspend)
+ udc->driver->suspend(&udc->gadget);
+}
+
+static void bus_resume(struct fsl_udc *udc)
+{
+ udc->usb_state = udc->resume_state;
+ udc->resume_state = 0;
+
+ /* report resume to the driver, serial.c does not support this */
+ if (udc->driver->resume)
+ udc->driver->resume(&udc->gadget);
+}
+
+/* Clear up all ep queues */
+static int reset_queues(struct fsl_udc *udc)
+{
+ u8 pipe;
+
+ for (pipe = 0; pipe < udc->max_pipes; pipe++)
+ udc_reset_ep_queue(udc, pipe);
+
+ /* report disconnect; the driver is already quiesced */
+ udc->driver->disconnect(&udc->gadget);
+
+ return 0;
+}
+
+/* Process reset interrupt */
+static void reset_irq(struct fsl_udc *udc)
+{
+ u32 temp;
+ unsigned long timeout;
+
+ /* Clear the device address */
+ temp = fsl_readl(&dr_regs->deviceaddr);
+ fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr);
+
+ udc->device_address = 0;
+
+ /* Clear usb state */
+ udc->resume_state = 0;
+ udc->ep0_dir = 0;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->remote_wakeup = 0; /* default to 0 on reset */
+ udc->gadget.b_hnp_enable = 0;
+ udc->gadget.a_hnp_support = 0;
+ udc->gadget.a_alt_hnp_support = 0;
+
+ /* Clear all the setup token semaphores */
+ temp = fsl_readl(&dr_regs->endptsetupstat);
+ fsl_writel(temp, &dr_regs->endptsetupstat);
+
+ /* Clear all the endpoint complete status bits */
+ temp = fsl_readl(&dr_regs->endptcomplete);
+ fsl_writel(temp, &dr_regs->endptcomplete);
+
+ timeout = jiffies + 100;
+ while (fsl_readl(&dr_regs->endpointprime)) {
+ /* Wait until all endptprime bits cleared */
+ if (time_after(jiffies, timeout)) {
+ ERR("Timeout for reset\n");
+ break;
+ }
+ cpu_relax();
+ }
+
+ /* Write 1s to the flush register */
+ fsl_writel(0xffffffff, &dr_regs->endptflush);
+
+ if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
+ VDBG("Bus reset");
+ /* Bus is reseting */
+ udc->bus_reset = 1;
+ /* Reset all the queues, include XD, dTD, EP queue
+ * head and TR Queue */
+ reset_queues(udc);
+ udc->usb_state = USB_STATE_DEFAULT;
+ } else {
+ VDBG("Controller reset");
+ /* initialize usb hw reg except for regs for EP, not
+ * touch usbintr reg */
+ dr_controller_setup(udc);
+
+ /* Reset all internal used Queues */
+ reset_queues(udc);
+
+ ep0_setup(udc);
+
+ /* Enable DR IRQ reg, Set Run bit, change udc state */
+ dr_controller_run(udc);
+ udc->usb_state = USB_STATE_ATTACHED;
+ }
+}
+
+/*
+ * USB device controller interrupt handler
+ */
+static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+{
+ struct fsl_udc *udc = _udc;
+ u32 irq_src;
+ irqreturn_t status = IRQ_NONE;
+ unsigned long flags;
+
+ /* Disable ISR for OTG host mode */
+ if (udc->stopped)
+ return IRQ_NONE;
+ spin_lock_irqsave(&udc->lock, flags);
+ irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
+ /* Clear notification bits */
+ fsl_writel(irq_src, &dr_regs->usbsts);
+
+ /* VDBG("irq_src [0x%8x]", irq_src); */
+
+ /* Need to resume? */
+ if (udc->usb_state == USB_STATE_SUSPENDED)
+ if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
+ bus_resume(udc);
+
+ /* USB Interrupt */
+ if (irq_src & USB_STS_INT) {
+ VDBG("Packet int");
+ /* Setup package, we only support ep0 as control ep */
+ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
+ tripwire_handler(udc, 0,
+ (u8 *) (&udc->local_setup_buff));
+ setup_received_irq(udc, &udc->local_setup_buff);
+ status = IRQ_HANDLED;
+ }
+
+ /* completion of dtd */
+ if (fsl_readl(&dr_regs->endptcomplete)) {
+ dtd_complete_irq(udc);
+ status = IRQ_HANDLED;
+ }
+ }
+
+ /* SOF (for ISO transfer) */
+ if (irq_src & USB_STS_SOF) {
+ status = IRQ_HANDLED;
+ }
+
+ /* Port Change */
+ if (irq_src & USB_STS_PORT_CHANGE) {
+ port_change_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ /* Reset Received */
+ if (irq_src & USB_STS_RESET) {
+ reset_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ /* Sleep Enable (Suspend) */
+ if (irq_src & USB_STS_SUSPEND) {
+ suspend_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+ VDBG("Error IRQ %x ", irq_src);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return status;
+}
+
+/*----------------------------------------------------------------*
+ * Hook to gadget drivers
+ * Called by initialization code of gadget drivers
+*----------------------------------------------------------------*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ int retval = -ENODEV;
+ unsigned long flags = 0;
+
+ if (!udc_controller)
+ return -ENODEV;
+
+ if (!driver || (driver->speed != USB_SPEED_FULL
+ && driver->speed != USB_SPEED_HIGH)
+ || !driver->bind || !driver->disconnect
+ || !driver->setup)
+ return -EINVAL;
+
+ if (udc_controller->driver)
+ return -EBUSY;
+
+ /* lock is needed but whether should use this lock or another */
+ spin_lock_irqsave(&udc_controller->lock, flags);
+
+ driver->driver.bus = 0;
+ /* hook up the driver */
+ udc_controller->driver = driver;
+ udc_controller->gadget.dev.driver = &driver->driver;
+ spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+ /* bind udc driver to gadget driver */
+ retval = driver->bind(&udc_controller->gadget);
+ if (retval) {
+ VDBG("bind to %s --> %d", driver->driver.name, retval);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+ goto out;
+ }
+
+ /* Enable DR IRQ reg and Set usbcmd reg Run bit */
+ dr_controller_run(udc_controller);
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_state = WAIT_FOR_SETUP;
+ udc_controller->ep0_dir = 0;
+ printk(KERN_INFO "%s: bind to driver %s \n",
+ udc_controller->gadget.name, driver->driver.name);
+
+out:
+ if (retval)
+ printk("retval %d \n", retval);
+ return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/* Disconnect from gadget driver */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct fsl_ep *loop_ep;
+ unsigned long flags;
+
+ if (!udc_controller)
+ return -ENODEV;
+
+ if (!driver || driver != udc_controller->driver || !driver->unbind)
+ return -EINVAL;
+
+#ifdef CONFIG_USB_OTG
+ if (udc_controller->transceiver)
+ (void)otg_set_peripheral(udc_controller->transceiver, 0);
+#endif
+
+ /* stop DR, disable intr */
+ dr_controller_stop(udc_controller);
+
+ /* in fact, no needed */
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_state = WAIT_FOR_SETUP;
+ udc_controller->ep0_dir = 0;
+
+ /* stand operation */
+ spin_lock_irqsave(&udc_controller->lock, flags);
+ udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+ nuke(&udc_controller->eps[0], -ESHUTDOWN);
+ list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
+ ep.ep_list)
+ nuke(loop_ep, -ESHUTDOWN);
+ spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+ /* unbind gadget and unhook driver. */
+ driver->unbind(&udc_controller->gadget);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+
+ printk("unregistered gadget driver '%s'\r\n", driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------
+ PROC File System Support
+-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char proc_filename[] = "driver/fsl_usb2_udc";
+
+static int fsl_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *_dev)
+{
+ char *buf = page;
+ char *next = buf;
+ unsigned size = count;
+ unsigned long flags;
+ int t, i;
+ u32 tmp_reg;
+ struct fsl_ep *ep = NULL;
+ struct fsl_req *req;
+
+ struct fsl_udc *udc = udc_controller;
+ if (off != 0)
+ return 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* ------basic driver infomation ---- */
+ t = scnprintf(next, size,
+ DRIVER_DESC "\n"
+ "%s version: %s\n"
+ "Gadget driver: %s\n\n",
+ driver_name, DRIVER_VERSION,
+ udc->driver ? udc->driver->driver.name : "(none)");
+ size -= t;
+ next += t;
+
+ /* ------ DR Registers ----- */
+ tmp_reg = fsl_readl(&dr_regs->usbcmd);
+ t = scnprintf(next, size,
+ "USBCMD reg:\n"
+ "SetupTW: %d\n"
+ "Run/Stop: %s\n\n",
+ (tmp_reg & USB_CMD_SUTW) ? 1 : 0,
+ (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbsts);
+ t = scnprintf(next, size,
+ "USB Status Reg:\n"
+ "Dr Suspend: %d" "Reset Received: %d" "System Error: %s"
+ "USB Error Interrupt: %s\n\n",
+ (tmp_reg & USB_STS_SUSPEND) ? 1 : 0,
+ (tmp_reg & USB_STS_RESET) ? 1 : 0,
+ (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal",
+ (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbintr);
+ t = scnprintf(next, size,
+ "USB Intrrupt Enable Reg:\n"
+ "Sleep Enable: %d" "SOF Received Enable: %d"
+ "Reset Enable: %d\n"
+ "System Error Enable: %d"
+ "Port Change Dectected Enable: %d\n"
+ "USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n",
+ (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
+ (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_RESET_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_INT_EN) ? 1 : 0);
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->frindex);
+ t = scnprintf(next, size,
+ "USB Frame Index Reg:" "Frame Number is 0x%x\n\n",
+ (tmp_reg & USB_FRINDEX_MASKS));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->deviceaddr);
+ t = scnprintf(next, size,
+ "USB Device Address Reg:" "Device Addr is 0x%x\n\n",
+ (tmp_reg & USB_DEVICE_ADDRESS_MASK));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->endpointlistaddr);
+ t = scnprintf(next, size,
+ "USB Endpoint List Address Reg:"
+ "Device Addr is 0x%x\n\n",
+ (tmp_reg & USB_EP_LIST_ADDRESS_MASK));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->portsc1);
+ t = scnprintf(next, size,
+ "USB Port Status&Control Reg:\n"
+ "Port Transceiver Type : %s" "Port Speed: %s \n"
+ "PHY Low Power Suspend: %s" "Port Reset: %s"
+ "Port Suspend Mode: %s \n" "Over-current Change: %s"
+ "Port Enable/Disable Change: %s\n"
+ "Port Enabled/Disabled: %s"
+ "Current Connect Status: %s\n\n", ( {
+ char *s;
+ switch (tmp_reg & PORTSCX_PTS_FSLS) {
+ case PORTSCX_PTS_UTMI:
+ s = "UTMI"; break;
+ case PORTSCX_PTS_ULPI:
+ s = "ULPI "; break;
+ case PORTSCX_PTS_FSLS:
+ s = "FS/LS Serial"; break;
+ default:
+ s = "None"; break;
+ }
+ s;} ), ( {
+ char *s;
+ switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) {
+ case PORTSCX_PORT_SPEED_FULL:
+ s = "Full Speed"; break;
+ case PORTSCX_PORT_SPEED_LOW:
+ s = "Low Speed"; break;
+ case PORTSCX_PORT_SPEED_HIGH:
+ s = "High Speed"; break;
+ default:
+ s = "Undefined"; break;
+ }
+ s;
+ } ),
+ (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ?
+ "Normal PHY mode" : "Low power mode",
+ (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" :
+ "Not in Reset",
+ (tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in",
+ (tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" :
+ "No",
+ (tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" :
+ "Not change",
+ (tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" :
+ "Not correct",
+ (tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ?
+ "Attached" : "Not-Att");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbmode);
+ t = scnprintf(next, size,
+ "USB Mode Reg:" "Controller Mode is : %s\n\n", ( {
+ char *s;
+ switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) {
+ case USB_MODE_CTRL_MODE_IDLE:
+ s = "Idle"; break;
+ case USB_MODE_CTRL_MODE_DEVICE:
+ s = "Device Controller"; break;
+ case USB_MODE_CTRL_MODE_HOST:
+ s = "Host Controller"; break;
+ default:
+ s = "None"; break;
+ }
+ s;
+ } ));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->endptsetupstat);
+ t = scnprintf(next, size,
+ "Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n",
+ (tmp_reg & EP_SETUP_STATUS_MASK));
+ size -= t;
+ next += t;
+
+ for (i = 0; i < udc->max_ep / 2; i++) {
+ tmp_reg = fsl_readl(&dr_regs->endptctrl[i]);
+ t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n",
+ i, tmp_reg);
+ size -= t;
+ next += t;
+ }
+ tmp_reg = fsl_readl(&dr_regs->endpointprime);
+ t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = usb_sys_regs->snoop1;
+ t = scnprintf(next, size, "\nSnoop1 Reg : = [0x%x]\n\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = usb_sys_regs->control;
+ t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n",
+ tmp_reg);
+ size -= t;
+ next += t;
+
+ /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
+ ep = &udc->eps[0];
+ t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n",
+ ep->ep.name, ep_maxpacket(ep), ep_index(ep));
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size, "its req queue is empty\n\n");
+ size -= t;
+ next += t;
+ } else {
+ list_for_each_entry(req, &ep->queue, queue) {
+ t = scnprintf(next, size,
+ "req %p actual 0x%x length 0x%x buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ size -= t;
+ next += t;
+ }
+ }
+ /* other gadget->eplist ep */
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ if (ep->desc) {
+ t = scnprintf(next, size,
+ "\nFor %s Maxpkt is 0x%x "
+ "index is 0x%x\n",
+ ep->ep.name, ep_maxpacket(ep),
+ ep_index(ep));
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size,
+ "its req queue is empty\n\n");
+ size -= t;
+ next += t;
+ } else {
+ list_for_each_entry(req, &ep->queue, queue) {
+ t = scnprintf(next, size,
+ "req %p actual 0x%x length"
+ "0x%x buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ size -= t;
+ next += t;
+ } /* end for each_entry of ep req */
+ } /* end for else */
+ } /* end for if(ep->queue) */
+ } /* end (ep->desc) */
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ *eof = 1;
+ return count - size;
+}
+
+#define create_proc_file() create_proc_read_entry(proc_filename, \
+ 0, NULL, fsl_proc_read, NULL)
+
+#define remove_proc_file() remove_proc_entry(proc_filename, NULL)
+
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_file() do {} while (0)
+#define remove_proc_file() do {} while (0)
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/*-------------------------------------------------------------------------*/
+
+/* Release udc structures */
+static void fsl_udc_release(struct device *dev)
+{
+ complete(udc_controller->done);
+ dma_free_coherent(dev, udc_controller->ep_qh_size,
+ udc_controller->ep_qh, udc_controller->ep_qh_dma);
+ kfree(udc_controller);
+}
+
+/******************************************************************
+ Internal structure setup functions
+*******************************************************************/
+/*------------------------------------------------------------------
+ * init resource for globle controller
+ * Return the udc handle on success or NULL on failure
+ ------------------------------------------------------------------*/
+static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev)
+{
+ struct fsl_udc *udc;
+ struct fsl_usb2_platform_data *pdata;
+ size_t size;
+
+ udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
+ if (udc == NULL) {
+ ERR("malloc udc failed\n");
+ return NULL;
+ }
+
+ pdata = pdev->dev.platform_data;
+ udc->phy_mode = pdata->phy_mode;
+ /* max_ep_nr is bidirectional ep number, max_ep doubles the number */
+ udc->max_ep = pdata->max_ep_nr * 2;
+
+ udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
+ if (!udc->eps) {
+ ERR("malloc fsl_ep failed\n");
+ goto cleanup;
+ }
+
+ /* initialized QHs, take care of alignment */
+ size = udc->max_ep * sizeof(struct ep_queue_head);
+ if (size < QH_ALIGNMENT)
+ size = QH_ALIGNMENT;
+ else if ((size % QH_ALIGNMENT) != 0) {
+ size += QH_ALIGNMENT + 1;
+ size &= ~(QH_ALIGNMENT - 1);
+ }
+ udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
+ &udc->ep_qh_dma, GFP_KERNEL);
+ if (!udc->ep_qh) {
+ ERR("malloc QHs for udc failed\n");
+ kfree(udc->eps);
+ goto cleanup;
+ }
+
+ udc->ep_qh_size = size;
+
+ /* Initialize ep0 status request structure */
+ /* FIXME: fsl_alloc_request() ignores ep argument */
+ udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
+ struct fsl_req, req);
+ /* allocate a small amount of memory to get valid address */
+ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+ udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf);
+
+ udc->resume_state = USB_STATE_NOTATTACHED;
+ udc->usb_state = USB_STATE_POWERED;
+ udc->ep0_dir = 0;
+ udc->remote_wakeup = 0; /* default to 0 on reset */
+ spin_lock_init(&udc->lock);
+
+ return udc;
+
+cleanup:
+ kfree(udc);
+ return NULL;
+}
+
+/*----------------------------------------------------------------
+ * Setup the fsl_ep struct for eps
+ * Link fsl_ep->ep to gadget->ep_list
+ * ep0out is not used so do nothing here
+ * ep0in should be taken care
+ *--------------------------------------------------------------*/
+static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
+ char *name, int link)
+{
+ struct fsl_ep *ep = &udc->eps[index];
+
+ ep->udc = udc;
+ strcpy(ep->name, name);
+ ep->ep.name = ep->name;
+
+ ep->ep.ops = &fsl_ep_ops;
+ ep->stopped = 0;
+
+ /* for ep0: maxP defined in desc
+ * for other eps, maxP is set by epautoconfig() called by gadget layer
+ */
+ ep->ep.maxpacket = (unsigned short) ~0;
+
+ /* the queue lists any req for this ep */
+ INIT_LIST_HEAD(&ep->queue);
+
+ /* gagdet.ep_list used for ep_autoconfig so no ep0 */
+ if (link)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ ep->gadget = &udc->gadget;
+ ep->qh = &udc->ep_qh[index];
+
+ return 0;
+}
+
+/* Driver probe function
+ * all intialize operations implemented here except enabling usb_intr reg
+ */
+static int __init fsl_udc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret = -ENODEV;
+ unsigned int i;
+
+ if (strcmp(pdev->name, driver_name)) {
+ VDBG("Wrong device\n");
+ return -ENODEV;
+ }
+
+ /* board setup should have been done in the platform code */
+
+ /* Initialize the udc structure including QH member and other member */
+ udc_controller = struct_udc_setup(pdev);
+ if (!udc_controller) {
+ VDBG("udc_controller is NULL \n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ driver_name)) {
+ ERR("request mem region for %s failed \n", pdev->name);
+ return -EBUSY;
+ }
+
+ dr_regs = ioremap(res->start, res->end - res->start + 1);
+ if (!dr_regs) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ usb_sys_regs = (struct usb_sys_interface *)
+ ((u32)dr_regs + USB_DR_SYS_OFFSET);
+
+ udc_controller->irq = platform_get_irq(pdev, 0);
+ if (!udc_controller->irq) {
+ ret = -ENODEV;
+ goto err2;
+ }
+
+ ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ,
+ driver_name, udc_controller);
+ if (ret != 0) {
+ ERR("cannot request irq %d err %d \n",
+ udc_controller->irq, ret);
+ goto err2;
+ }
+
+ /* initialize usb hw reg except for regs for EP,
+ * leave usbintr reg untouched */
+ dr_controller_setup(udc_controller);
+
+ /* Setup gadget structure */
+ udc_controller->gadget.ops = &fsl_gadget_ops;
+ udc_controller->gadget.is_dualspeed = 1;
+ udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
+ INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
+ udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+ udc_controller->gadget.name = driver_name;
+
+ /* Setup gadget.dev and register with kernel */
+ strcpy(udc_controller->gadget.dev.bus_id, "gadget");
+ udc_controller->gadget.dev.release = fsl_udc_release;
+ udc_controller->gadget.dev.parent = &pdev->dev;
+ ret = device_register(&udc_controller->gadget.dev);
+ if (ret < 0)
+ goto err3;
+
+ /* setup QH and epctrl for ep0 */
+ ep0_setup(udc_controller);
+
+ /* setup udc->eps[] for ep0 */
+ struct_ep_setup(udc_controller, 0, "ep0", 0);
+ /* for ep0: the desc defined here;
+ * for other eps, gadget layer called ep_enable with defined desc
+ */
+ udc_controller->eps[0].desc = &fsl_ep0_desc;
+ udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD;
+
+ /* setup the udc->eps[] for non-control endpoints and link
+ * to gadget.ep_list */
+ for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) {
+ char name[14];
+
+ sprintf(name, "ep%dout", i);
+ struct_ep_setup(udc_controller, i * 2, name, 1);
+ sprintf(name, "ep%din", i);
+ struct_ep_setup(udc_controller, i * 2 + 1, name, 1);
+ }
+
+ /* use dma_pool for TD management */
+ udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev,
+ sizeof(struct ep_td_struct),
+ DTD_ALIGNMENT, UDC_DMA_BOUNDARY);
+ if (udc_controller->td_pool == NULL) {
+ ret = -ENOMEM;
+ goto err4;
+ }
+ create_proc_file();
+ return 0;
+
+err4:
+ device_unregister(&udc_controller->gadget.dev);
+err3:
+ free_irq(udc_controller->irq, udc_controller);
+err2:
+ iounmap(dr_regs);
+err1:
+ release_mem_region(res->start, res->end - res->start + 1);
+ return ret;
+}
+
+/* Driver removal function
+ * Free resources and finish pending transactions
+ */
+static int __exit fsl_udc_remove(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ DECLARE_COMPLETION(done);
+
+ if (!udc_controller)
+ return -ENODEV;
+ udc_controller->done = &done;
+
+ /* DR has been stopped in usb_gadget_unregister_driver() */
+ remove_proc_file();
+
+ /* Free allocated memory */
+ kfree(udc_controller->status_req->req.buf);
+ kfree(udc_controller->status_req);
+ kfree(udc_controller->eps);
+
+ dma_pool_destroy(udc_controller->td_pool);
+ free_irq(udc_controller->irq, udc_controller);
+ iounmap(dr_regs);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ device_unregister(&udc_controller->gadget.dev);
+ /* free udc --wait for the release() finished */
+ wait_for_completion(&done);
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Modify Power management attributes
+ * Used by OTG statemachine to disable gadget temporarily
+ -----------------------------------------------------------------*/
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dr_controller_stop(udc_controller);
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Invoked on USB resume. May be called in_interrupt.
+ * Here we start the DR controller and enable the irq
+ *-----------------------------------------------------------------*/
+static int fsl_udc_resume(struct platform_device *pdev)
+{
+ /* Enable DR irq reg and set controller Run */
+ if (udc_controller->stopped) {
+ dr_controller_setup(udc_controller);
+ dr_controller_run(udc_controller);
+ }
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_state = WAIT_FOR_SETUP;
+ udc_controller->ep0_dir = 0;
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ Register entry point for the peripheral controller driver
+--------------------------------------------------------------------------*/
+
+static struct platform_driver udc_driver = {
+ .remove = __exit_p(fsl_udc_remove),
+ /* these suspend and resume are not usb suspend and resume */
+ .suspend = fsl_udc_suspend,
+ .resume = fsl_udc_resume,
+ .driver = {
+ .name = (char *)driver_name,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init udc_init(void)
+{
+ printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION);
+ return platform_driver_probe(&udc_driver, fsl_udc_probe);
+}
+
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+ platform_driver_unregister(&udc_driver);
+ printk("%s unregistered \n", driver_desc);
+}
+
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
new file mode 100644
index 000000000000..c6291e046507
--- /dev/null
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -0,0 +1,579 @@
+/*
+ * Freescale USB device/endpoint management registers
+ */
+#ifndef __FSL_USB2_UDC_H
+#define __FSL_USB2_UDC_H
+
+/* ### define USB registers here
+ */
+#define USB_MAX_CTRL_PAYLOAD 64
+#define USB_DR_SYS_OFFSET 0x400
+
+ /* USB DR device mode registers (Little Endian) */
+struct usb_dr_device {
+ /* Capability register */
+ u8 res1[256];
+ u16 caplength; /* Capability Register Length */
+ u16 hciversion; /* Host Controller Interface Version */
+ u32 hcsparams; /* Host Controller Structual Parameters */
+ u32 hccparams; /* Host Controller Capability Parameters */
+ u8 res2[20];
+ u32 dciversion; /* Device Controller Interface Version */
+ u32 dccparams; /* Device Controller Capability Parameters */
+ u8 res3[24];
+ /* Operation register */
+ u32 usbcmd; /* USB Command Register */
+ u32 usbsts; /* USB Status Register */
+ u32 usbintr; /* USB Interrupt Enable Register */
+ u32 frindex; /* Frame Index Register */
+ u8 res4[4];
+ u32 deviceaddr; /* Device Address */
+ u32 endpointlistaddr; /* Endpoint List Address Register */
+ u8 res5[4];
+ u32 burstsize; /* Master Interface Data Burst Size Register */
+ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
+ u8 res6[24];
+ u32 configflag; /* Configure Flag Register */
+ u32 portsc1; /* Port 1 Status and Control Register */
+ u8 res7[28];
+ u32 otgsc; /* On-The-Go Status and Control */
+ u32 usbmode; /* USB Mode Register */
+ u32 endptsetupstat; /* Endpoint Setup Status Register */
+ u32 endpointprime; /* Endpoint Initialization Register */
+ u32 endptflush; /* Endpoint Flush Register */
+ u32 endptstatus; /* Endpoint Status Register */
+ u32 endptcomplete; /* Endpoint Complete Register */
+ u32 endptctrl[6]; /* Endpoint Control Registers */
+};
+
+ /* USB DR host mode registers (Little Endian) */
+struct usb_dr_host {
+ /* Capability register */
+ u8 res1[256];
+ u16 caplength; /* Capability Register Length */
+ u16 hciversion; /* Host Controller Interface Version */
+ u32 hcsparams; /* Host Controller Structual Parameters */
+ u32 hccparams; /* Host Controller Capability Parameters */
+ u8 res2[20];
+ u32 dciversion; /* Device Controller Interface Version */
+ u32 dccparams; /* Device Controller Capability Parameters */
+ u8 res3[24];
+ /* Operation register */
+ u32 usbcmd; /* USB Command Register */
+ u32 usbsts; /* USB Status Register */
+ u32 usbintr; /* USB Interrupt Enable Register */
+ u32 frindex; /* Frame Index Register */
+ u8 res4[4];
+ u32 periodiclistbase; /* Periodic Frame List Base Address Register */
+ u32 asynclistaddr; /* Current Asynchronous List Address Register */
+ u8 res5[4];
+ u32 burstsize; /* Master Interface Data Burst Size Register */
+ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
+ u8 res6[24];
+ u32 configflag; /* Configure Flag Register */
+ u32 portsc1; /* Port 1 Status and Control Register */
+ u8 res7[28];
+ u32 otgsc; /* On-The-Go Status and Control */
+ u32 usbmode; /* USB Mode Register */
+ u32 endptsetupstat; /* Endpoint Setup Status Register */
+ u32 endpointprime; /* Endpoint Initialization Register */
+ u32 endptflush; /* Endpoint Flush Register */
+ u32 endptstatus; /* Endpoint Status Register */
+ u32 endptcomplete; /* Endpoint Complete Register */
+ u32 endptctrl[6]; /* Endpoint Control Registers */
+};
+
+ /* non-EHCI USB system interface registers (Big Endian) */
+struct usb_sys_interface {
+ u32 snoop1;
+ u32 snoop2;
+ u32 age_cnt_thresh; /* Age Count Threshold Register */
+ u32 pri_ctrl; /* Priority Control Register */
+ u32 si_ctrl; /* System Interface Control Register */
+ u8 res[236];
+ u32 control; /* General Purpose Control Register */
+};
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP 0
+#define DATA_STATE_XMIT 1
+#define DATA_STATE_NEED_ZLP 2
+#define WAIT_FOR_OUT_STATUS 3
+#define DATA_STATE_RECV 4
+
+/* Frame Index Register Bit Masks */
+#define USB_FRINDEX_MASKS 0x3fff
+/* USB CMD Register Bit Masks */
+#define USB_CMD_RUN_STOP 0x00000001
+#define USB_CMD_CTRL_RESET 0x00000002
+#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010
+#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020
+#define USB_CMD_INT_AA_DOORBELL 0x00000040
+#define USB_CMD_ASP 0x00000300
+#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800
+#define USB_CMD_SUTW 0x00002000
+#define USB_CMD_ATDTW 0x00004000
+#define USB_CMD_ITC 0x00FF0000
+
+/* bit 15,3,2 are frame list size */
+#define USB_CMD_FRAME_SIZE_1024 0x00000000
+#define USB_CMD_FRAME_SIZE_512 0x00000004
+#define USB_CMD_FRAME_SIZE_256 0x00000008
+#define USB_CMD_FRAME_SIZE_128 0x0000000C
+#define USB_CMD_FRAME_SIZE_64 0x00008000
+#define USB_CMD_FRAME_SIZE_32 0x00008004
+#define USB_CMD_FRAME_SIZE_16 0x00008008
+#define USB_CMD_FRAME_SIZE_8 0x0000800C
+
+/* bit 9-8 are async schedule park mode count */
+#define USB_CMD_ASP_00 0x00000000
+#define USB_CMD_ASP_01 0x00000100
+#define USB_CMD_ASP_10 0x00000200
+#define USB_CMD_ASP_11 0x00000300
+#define USB_CMD_ASP_BIT_POS 8
+
+/* bit 23-16 are interrupt threshold control */
+#define USB_CMD_ITC_NO_THRESHOLD 0x00000000
+#define USB_CMD_ITC_1_MICRO_FRM 0x00010000
+#define USB_CMD_ITC_2_MICRO_FRM 0x00020000
+#define USB_CMD_ITC_4_MICRO_FRM 0x00040000
+#define USB_CMD_ITC_8_MICRO_FRM 0x00080000
+#define USB_CMD_ITC_16_MICRO_FRM 0x00100000
+#define USB_CMD_ITC_32_MICRO_FRM 0x00200000
+#define USB_CMD_ITC_64_MICRO_FRM 0x00400000
+#define USB_CMD_ITC_BIT_POS 16
+
+/* USB STS Register Bit Masks */
+#define USB_STS_INT 0x00000001
+#define USB_STS_ERR 0x00000002
+#define USB_STS_PORT_CHANGE 0x00000004
+#define USB_STS_FRM_LST_ROLL 0x00000008
+#define USB_STS_SYS_ERR 0x00000010
+#define USB_STS_IAA 0x00000020
+#define USB_STS_RESET 0x00000040
+#define USB_STS_SOF 0x00000080
+#define USB_STS_SUSPEND 0x00000100
+#define USB_STS_HC_HALTED 0x00001000
+#define USB_STS_RCL 0x00002000
+#define USB_STS_PERIODIC_SCHEDULE 0x00004000
+#define USB_STS_ASYNC_SCHEDULE 0x00008000
+
+/* USB INTR Register Bit Masks */
+#define USB_INTR_INT_EN 0x00000001
+#define USB_INTR_ERR_INT_EN 0x00000002
+#define USB_INTR_PTC_DETECT_EN 0x00000004
+#define USB_INTR_FRM_LST_ROLL_EN 0x00000008
+#define USB_INTR_SYS_ERR_EN 0x00000010
+#define USB_INTR_ASYN_ADV_EN 0x00000020
+#define USB_INTR_RESET_EN 0x00000040
+#define USB_INTR_SOF_EN 0x00000080
+#define USB_INTR_DEVICE_SUSPEND 0x00000100
+
+/* Device Address bit masks */
+#define USB_DEVICE_ADDRESS_MASK 0xFE000000
+#define USB_DEVICE_ADDRESS_BIT_POS 25
+
+/* endpoint list address bit masks */
+#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
+
+/* PORTSCX Register Bit Masks */
+#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001
+#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002
+#define PORTSCX_PORT_ENABLE 0x00000004
+#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008
+#define PORTSCX_OVER_CURRENT_ACT 0x00000010
+#define PORTSCX_OVER_CURRENT_CHG 0x00000020
+#define PORTSCX_PORT_FORCE_RESUME 0x00000040
+#define PORTSCX_PORT_SUSPEND 0x00000080
+#define PORTSCX_PORT_RESET 0x00000100
+#define PORTSCX_LINE_STATUS_BITS 0x00000C00
+#define PORTSCX_PORT_POWER 0x00001000
+#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000
+#define PORTSCX_PORT_TEST_CTRL 0x000F0000
+#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000
+#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000
+#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000
+#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000
+#define PORTSCX_PORT_FORCE_FULL_SPEED 0x01000000
+#define PORTSCX_PORT_SPEED_MASK 0x0C000000
+#define PORTSCX_PORT_WIDTH 0x10000000
+#define PORTSCX_PHY_TYPE_SEL 0xC0000000
+
+/* bit 11-10 are line status */
+#define PORTSCX_LINE_STATUS_SE0 0x00000000
+#define PORTSCX_LINE_STATUS_JSTATE 0x00000400
+#define PORTSCX_LINE_STATUS_KSTATE 0x00000800
+#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00
+#define PORTSCX_LINE_STATUS_BIT_POS 10
+
+/* bit 15-14 are port indicator control */
+#define PORTSCX_PIC_OFF 0x00000000
+#define PORTSCX_PIC_AMBER 0x00004000
+#define PORTSCX_PIC_GREEN 0x00008000
+#define PORTSCX_PIC_UNDEF 0x0000C000
+#define PORTSCX_PIC_BIT_POS 14
+
+/* bit 19-16 are port test control */
+#define PORTSCX_PTC_DISABLE 0x00000000
+#define PORTSCX_PTC_JSTATE 0x00010000
+#define PORTSCX_PTC_KSTATE 0x00020000
+#define PORTSCX_PTC_SEQNAK 0x00030000
+#define PORTSCX_PTC_PACKET 0x00040000
+#define PORTSCX_PTC_FORCE_EN 0x00050000
+#define PORTSCX_PTC_BIT_POS 16
+
+/* bit 27-26 are port speed */
+#define PORTSCX_PORT_SPEED_FULL 0x00000000
+#define PORTSCX_PORT_SPEED_LOW 0x04000000
+#define PORTSCX_PORT_SPEED_HIGH 0x08000000
+#define PORTSCX_PORT_SPEED_UNDEF 0x0C000000
+#define PORTSCX_SPEED_BIT_POS 26
+
+/* bit 28 is parallel transceiver width for UTMI interface */
+#define PORTSCX_PTW 0x10000000
+#define PORTSCX_PTW_8BIT 0x00000000
+#define PORTSCX_PTW_16BIT 0x10000000
+
+/* bit 31-30 are port transceiver select */
+#define PORTSCX_PTS_UTMI 0x00000000
+#define PORTSCX_PTS_ULPI 0x80000000
+#define PORTSCX_PTS_FSLS 0xC0000000
+#define PORTSCX_PTS_BIT_POS 30
+
+/* otgsc Register Bit Masks */
+#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001
+#define OTGSC_CTRL_VUSB_CHARGE 0x00000002
+#define OTGSC_CTRL_OTG_TERM 0x00000008
+#define OTGSC_CTRL_DATA_PULSING 0x00000010
+#define OTGSC_STS_USB_ID 0x00000100
+#define OTGSC_STS_A_VBUS_VALID 0x00000200
+#define OTGSC_STS_A_SESSION_VALID 0x00000400
+#define OTGSC_STS_B_SESSION_VALID 0x00000800
+#define OTGSC_STS_B_SESSION_END 0x00001000
+#define OTGSC_STS_1MS_TOGGLE 0x00002000
+#define OTGSC_STS_DATA_PULSING 0x00004000
+#define OTGSC_INTSTS_USB_ID 0x00010000
+#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000
+#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000
+#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000
+#define OTGSC_INTSTS_B_SESSION_END 0x00100000
+#define OTGSC_INTSTS_1MS 0x00200000
+#define OTGSC_INTSTS_DATA_PULSING 0x00400000
+#define OTGSC_INTR_USB_ID 0x01000000
+#define OTGSC_INTR_A_VBUS_VALID 0x02000000
+#define OTGSC_INTR_A_SESSION_VALID 0x04000000
+#define OTGSC_INTR_B_SESSION_VALID 0x08000000
+#define OTGSC_INTR_B_SESSION_END 0x10000000
+#define OTGSC_INTR_1MS_TIMER 0x20000000
+#define OTGSC_INTR_DATA_PULSING 0x40000000
+
+/* USB MODE Register Bit Masks */
+#define USB_MODE_CTRL_MODE_IDLE 0x00000000
+#define USB_MODE_CTRL_MODE_DEVICE 0x00000002
+#define USB_MODE_CTRL_MODE_HOST 0x00000003
+#define USB_MODE_CTRL_MODE_RSV 0x00000001
+#define USB_MODE_SETUP_LOCK_OFF 0x00000008
+#define USB_MODE_STREAM_DISABLE 0x00000010
+/* Endpoint Flush Register */
+#define EPFLUSH_TX_OFFSET 0x00010000
+#define EPFLUSH_RX_OFFSET 0x00000000
+
+/* Endpoint Setup Status bit masks */
+#define EP_SETUP_STATUS_MASK 0x0000003F
+#define EP_SETUP_STATUS_EP0 0x00000001
+
+/* ENDPOINTCTRLx Register Bit Masks */
+#define EPCTRL_TX_ENABLE 0x00800000
+#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */
+#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */
+#define EPCTRL_TX_TYPE 0x000C0000
+#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */
+#define EPCTRL_TX_EP_STALL 0x00010000
+#define EPCTRL_RX_ENABLE 0x00000080
+#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */
+#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */
+#define EPCTRL_RX_TYPE 0x0000000C
+#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */
+#define EPCTRL_RX_EP_STALL 0x00000001
+
+/* bit 19-18 and 3-2 are endpoint type */
+#define EPCTRL_EP_TYPE_CONTROL 0
+#define EPCTRL_EP_TYPE_ISO 1
+#define EPCTRL_EP_TYPE_BULK 2
+#define EPCTRL_EP_TYPE_INTERRUPT 3
+#define EPCTRL_TX_EP_TYPE_SHIFT 18
+#define EPCTRL_RX_EP_TYPE_SHIFT 2
+
+/* SNOOPn Register Bit Masks */
+#define SNOOP_ADDRESS_MASK 0xFFFFF000
+#define SNOOP_SIZE_ZERO 0x00 /* snooping disable */
+#define SNOOP_SIZE_4KB 0x0B /* 4KB snoop size */
+#define SNOOP_SIZE_8KB 0x0C
+#define SNOOP_SIZE_16KB 0x0D
+#define SNOOP_SIZE_32KB 0x0E
+#define SNOOP_SIZE_64KB 0x0F
+#define SNOOP_SIZE_128KB 0x10
+#define SNOOP_SIZE_256KB 0x11
+#define SNOOP_SIZE_512KB 0x12
+#define SNOOP_SIZE_1MB 0x13
+#define SNOOP_SIZE_2MB 0x14
+#define SNOOP_SIZE_4MB 0x15
+#define SNOOP_SIZE_8MB 0x16
+#define SNOOP_SIZE_16MB 0x17
+#define SNOOP_SIZE_32MB 0x18
+#define SNOOP_SIZE_64MB 0x19
+#define SNOOP_SIZE_128MB 0x1A
+#define SNOOP_SIZE_256MB 0x1B
+#define SNOOP_SIZE_512MB 0x1C
+#define SNOOP_SIZE_1GB 0x1D
+#define SNOOP_SIZE_2GB 0x1E /* 2GB snoop size */
+
+/* pri_ctrl Register Bit Masks */
+#define PRI_CTRL_PRI_LVL1 0x0000000C
+#define PRI_CTRL_PRI_LVL0 0x00000003
+
+/* si_ctrl Register Bit Masks */
+#define SI_CTRL_ERR_DISABLE 0x00000010
+#define SI_CTRL_IDRC_DISABLE 0x00000008
+#define SI_CTRL_RD_SAFE_EN 0x00000004
+#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002
+#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001
+
+/* control Register Bit Masks */
+#define USB_CTRL_IOENB 0x00000004
+#define USB_CTRL_ULPI_INT0EN 0x00000001
+
+/* Endpoint Queue Head data struct
+ * Rem: all the variables of qh are LittleEndian Mode
+ * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr
+ */
+struct ep_queue_head {
+ u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len
+ and IOS(15) */
+ u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */
+ u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */
+ u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15),
+ MultO(11-10), STS (7-0) */
+ u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */
+ u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */
+ u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */
+ u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */
+ u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */
+ u32 res1;
+ u8 setup_buffer[8]; /* Setup data 8 bytes */
+ u32 res2[4];
+};
+
+/* Endpoint Queue Head Bit Masks */
+#define EP_QUEUE_HEAD_MULT_POS 30
+#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
+#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
+#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
+#define EP_QUEUE_HEAD_IOS 0x00008000
+#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
+#define EP_QUEUE_HEAD_IOC 0x00008000
+#define EP_QUEUE_HEAD_MULTO 0x00000C00
+#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
+#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
+#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
+#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
+#define EP_QUEUE_FRINDEX_MASK 0x000007FF
+#define EP_MAX_LENGTH_TRANSFER 0x4000
+
+/* Endpoint Transfer Descriptor data struct */
+/* Rem: all the variables of td are LittleEndian Mode */
+struct ep_td_struct {
+ u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set
+ indicate invalid */
+ u32 size_ioc_sts; /* Total bytes (30-16), IOC (15),
+ MultO(11-10), STS (7-0) */
+ u32 buff_ptr0; /* Buffer pointer Page 0 */
+ u32 buff_ptr1; /* Buffer pointer Page 1 */
+ u32 buff_ptr2; /* Buffer pointer Page 2 */
+ u32 buff_ptr3; /* Buffer pointer Page 3 */
+ u32 buff_ptr4; /* Buffer pointer Page 4 */
+ u32 res;
+ /* 32 bytes */
+ dma_addr_t td_dma; /* dma address for this td */
+ /* virtual address of next td specified in next_td_ptr */
+ struct ep_td_struct *next_td_virt;
+};
+
+/* Endpoint Transfer Descriptor bit Masks */
+#define DTD_NEXT_TERMINATE 0x00000001
+#define DTD_IOC 0x00008000
+#define DTD_STATUS_ACTIVE 0x00000080
+#define DTD_STATUS_HALTED 0x00000040
+#define DTD_STATUS_DATA_BUFF_ERR 0x00000020
+#define DTD_STATUS_TRANSACTION_ERR 0x00000008
+#define DTD_RESERVED_FIELDS 0x80007300
+#define DTD_ADDR_MASK 0xFFFFFFE0
+#define DTD_PACKET_SIZE 0x7FFF0000
+#define DTD_LENGTH_BIT_POS 16
+#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \
+ DTD_STATUS_DATA_BUFF_ERR | \
+ DTD_STATUS_TRANSACTION_ERR)
+/* Alignment requirements; must be a power of two */
+#define DTD_ALIGNMENT 0x20
+#define QH_ALIGNMENT 2048
+
+/* Controller dma boundary */
+#define UDC_DMA_BOUNDARY 0x1000
+
+/* -----------------------------------------------------------------------*/
+/* ##### enum data
+*/
+typedef enum {
+ e_ULPI,
+ e_UTMI_8BIT,
+ e_UTMI_16BIT,
+ e_SERIAL
+} e_PhyInterface;
+
+/*-------------------------------------------------------------------------*/
+
+/* ### driver private data
+ */
+struct fsl_req {
+ struct usb_request req;
+ struct list_head queue;
+ /* ep_queue() func will add
+ a request->queue into a udc_ep->queue 'd tail */
+ struct fsl_ep *ep;
+ unsigned mapped:1;
+
+ struct ep_td_struct *head, *tail; /* For dTD List
+ cpu endian Virtual addr */
+ unsigned int dtd_count;
+};
+
+#define REQ_UNCOMPLETE 1
+
+struct fsl_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ struct fsl_udc *udc;
+ struct ep_queue_head *qh;
+ const struct usb_endpoint_descriptor *desc;
+ struct usb_gadget *gadget;
+
+ char name[14];
+ unsigned stopped:1;
+};
+
+#define EP_DIR_IN 1
+#define EP_DIR_OUT 0
+
+struct fsl_udc {
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct fsl_ep *eps;
+ unsigned int max_ep;
+ unsigned int irq;
+
+ struct usb_ctrlrequest local_setup_buff;
+ spinlock_t lock;
+ struct otg_transceiver *transceiver;
+ unsigned softconnect:1;
+ unsigned vbus_active:1;
+ unsigned stopped:1;
+ unsigned remote_wakeup:1;
+
+ struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
+ struct fsl_req *status_req; /* ep0 status request */
+ struct dma_pool *td_pool; /* dma pool for DTD */
+ enum fsl_usb2_phy_modes phy_mode;
+
+ size_t ep_qh_size; /* size after alignment adjustment*/
+ dma_addr_t ep_qh_dma; /* dma address of QH */
+
+ u32 max_pipes; /* Device max pipes */
+ u32 max_use_endpts; /* Max endpointes to be used */
+ u32 bus_reset; /* Device is bus reseting */
+ u32 resume_state; /* USB state to resume */
+ u32 usb_state; /* USB current state */
+ u32 usb_next_state; /* USB next state */
+ u32 ep0_state; /* Endpoint zero state */
+ u32 ep0_dir; /* Endpoint zero direction: can be
+ USB_DIR_IN or USB_DIR_OUT */
+ u32 usb_sof_count; /* SOF count */
+ u32 errors; /* USB ERRORs count */
+ u8 device_address; /* Device USB address */
+
+ struct completion *done; /* to make sure release() is done */
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \
+ __FUNCTION__, ## args)
+#else
+#define DBG(fmt, args...) do{}while(0)
+#endif
+
+#if 0
+static void dump_msg(const char *label, const u8 * buf, unsigned int length)
+{
+ unsigned int start, num, i;
+ char line[52], *p;
+
+ if (length >= 512)
+ return;
+ DBG("%s, length %u:\n", label, length);
+ start = 0;
+ while (length > 0) {
+ num = min(length, 16u);
+ p = line;
+ for (i = 0; i < num; ++i) {
+ if (i == 8)
+ *p++ = ' ';
+ sprintf(p, " %02x", buf[i]);
+ p += 3;
+ }
+ *p = 0;
+ printk(KERN_DEBUG "%6x: %s\n", start, line);
+ buf += num;
+ start += num;
+ length -= num;
+ }
+}
+#endif
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(stuff...) do{}while(0)
+#endif
+
+#define ERR(stuff...) printk(KERN_ERR "udc: " stuff)
+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
+
+/*-------------------------------------------------------------------------*/
+
+/* ### Add board specific defines here
+ */
+
+/*
+ * ### pipe direction macro from device view
+ */
+#define USB_RECV 0 /* OUT EP */
+#define USB_SEND 1 /* IN EP */
+
+/*
+ * ### internal used help routines.
+ */
+#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF)
+#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
+#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+ USB_DIR_IN ):((EP)->desc->bEndpointAddress \
+ & USB_DIR_IN)==USB_DIR_IN)
+#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \
+ &udc->eps[pipe])
+#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \
+ * 2 + ((windex & USB_DIR_IN) ? 1 : 0))
+#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP))
+
+#endif
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 2e3d6620d216..d041b919e7b8 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -99,6 +99,12 @@
#define gadget_is_imx(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_FSL_USB2
+#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name)
+#else
+#define gadget_is_fsl_usb2(g) 0
+#endif
+
/* Mentor high speed function controller */
#ifdef CONFIG_USB_GADGET_MUSBHSFC
#define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name)
@@ -177,5 +183,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x17;
else if (gadget_is_husb2dev(gadget))
return 0x18;
+ else if (gadget_is_fsl_usb2(gadget))
+ return 0x19;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index f01890dc8751..2c043a1ea156 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -71,7 +71,7 @@
* by the host to interact with this device, and allocates endpoints to
* the different protocol interfaces. The controller driver virtualizes
* usb hardware so that the gadget drivers will be more portable.
- *
+ *
* This UDC hardware wants to implement a bit too much USB protocol, so
* it constrains the sorts of USB configuration change events that work.
* The errata for these chips are misleading; some "fixed" bugs from
@@ -141,7 +141,7 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");
#endif
/* ---------------------------------------------------------------------------
- * endpoint related parts of the api to the usb controller hardware,
+ * endpoint related parts of the api to the usb controller hardware,
* used by gadget driver; and the inner talker-to-hardware core.
* ---------------------------------------------------------------------------
*/
@@ -293,7 +293,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep,
#ifdef USE_DMA
/* for (some) bulk and ISO endpoints, try to get a DMA channel and
- * bind it to the endpoint. otherwise use PIO.
+ * bind it to the endpoint. otherwise use PIO.
*/
switch (ep->bmAttributes) {
case USB_ENDPOINT_XFER_ISOC:
@@ -304,7 +304,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep,
if (!use_dma || !ep->reg_drcmr)
break;
ep->dma = pxa_request_dma ((char *)_ep->name,
- (le16_to_cpu (desc->wMaxPacketSize) > 64)
+ (le16_to_cpu (desc->wMaxPacketSize) > 64)
? DMA_PRIO_MEDIUM /* some iso */
: DMA_PRIO_LOW,
dma_nodesc_handler, ep);
@@ -361,7 +361,7 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep)
*/
/*
- * pxa2xx_ep_alloc_request - allocate a request data structure
+ * pxa2xx_ep_alloc_request - allocate a request data structure
*/
static struct usb_request *
pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
@@ -378,7 +378,7 @@ pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
/*
- * pxa2xx_ep_free_request - deallocate a request data structure
+ * pxa2xx_ep_free_request - deallocate a request data structure
*/
static void
pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req)
@@ -1031,7 +1031,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
/*
- * nuke - dequeue ALL requests
+ * nuke - dequeue ALL requests
*/
static void nuke(struct pxa2xx_ep *ep, int status)
{
@@ -1136,16 +1136,16 @@ static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value)
ep->dev->req_pending = 0;
ep->dev->ep0state = EP0_STALL;
- /* and bulk/intr endpoints like dropping stalls too */
- } else {
- unsigned i;
- for (i = 0; i < 1000; i += 20) {
- if (*ep->reg_udccs & UDCCS_BI_SST)
- break;
- udelay(20);
- }
- }
- local_irq_restore(flags);
+ /* and bulk/intr endpoints like dropping stalls too */
+ } else {
+ unsigned i;
+ for (i = 0; i < 1000; i += 20) {
+ if (*ep->reg_udccs & UDCCS_BI_SST)
+ break;
+ udelay(20);
+ }
+ }
+ local_irq_restore(flags);
DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
return 0;
@@ -1216,7 +1216,7 @@ static struct usb_ep_ops pxa2xx_ep_ops = {
/* ---------------------------------------------------------------------------
- * device-scoped parts of the api to the usb controller hardware
+ * device-scoped parts of the api to the usb controller hardware
* ---------------------------------------------------------------------------
*/
@@ -1239,7 +1239,7 @@ static void udc_enable (struct pxa2xx_udc *);
static void udc_disable(struct pxa2xx_udc *);
/* We disable the UDC -- and its 48 MHz clock -- whenever it's not
- * in active use.
+ * in active use.
*/
static int pullup(struct pxa2xx_udc *udc, int is_active)
{
@@ -1464,24 +1464,10 @@ done:
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
-/* "function" sysfs attribute */
-static ssize_t
-show_function (struct device *_dev, struct device_attribute *attr, char *buf)
-{
- struct pxa2xx_udc *dev = dev_get_drvdata (_dev);
-
- if (!dev->driver
- || !dev->driver->function
- || strlen (dev->driver->function) > PAGE_SIZE)
- return 0;
- return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
-}
-static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
-
/*-------------------------------------------------------------------------*/
/*
- * udc_disable - disable USB device controller
+ * udc_disable - disable USB device controller
*/
static void udc_disable(struct pxa2xx_udc *dev)
{
@@ -1507,7 +1493,7 @@ static void udc_disable(struct pxa2xx_udc *dev)
/*
- * udc_reinit - initialize software state
+ * udc_reinit - initialize software state
*/
static void udc_reinit(struct pxa2xx_udc *dev)
{
@@ -1635,18 +1621,20 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
dev->gadget.dev.driver = &driver->driver;
dev->pullup = 1;
- device_add (&dev->gadget.dev);
+ retval = device_add (&dev->gadget.dev);
+ if (retval) {
+fail:
+ dev->driver = NULL;
+ dev->gadget.dev.driver = NULL;
+ return retval;
+ }
retval = driver->bind(&dev->gadget);
if (retval) {
DMSG("bind to driver %s --> error %d\n",
driver->driver.name, retval);
device_del (&dev->gadget.dev);
-
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return retval;
+ goto fail;
}
- device_create_file(dev->dev, &dev_attr_function);
/* ... then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
@@ -1704,7 +1692,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
dev->driver = NULL;
device_del (&dev->gadget.dev);
- device_remove_file(dev->dev, &dev_attr_function);
DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
dump_state(dev);
@@ -2474,12 +2461,12 @@ static struct pxa2xx_udc memory = {
#define IXP465_AD 0x00000200
/*
- * probe - binds to the platform device
+ * probe - binds to the platform device
*/
static int __init pxa2xx_udc_probe(struct platform_device *pdev)
{
struct pxa2xx_udc *dev = &memory;
- int retval, out_dma = 1, vbus_irq;
+ int retval, out_dma = 1, vbus_irq, irq;
u32 chiprev;
/* insist on Intel/ARM/XScale */
@@ -2522,7 +2509,11 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
return -ENODEV;
}
- pr_debug("%s: IRQ %d%s%s%s\n", driver_name, IRQ_USB,
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+
+ pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq,
dev->has_cfr ? "" : " (!cfr)",
out_dma ? "" : " (broken dma-out)",
SIZE_STR DMASTR
@@ -2570,11 +2561,11 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
dev->vbus = is_vbus_present();
/* irq setup after old hardware state is cleaned up */
- retval = request_irq(IRQ_USB, pxa2xx_udc_irq,
+ retval = request_irq(irq, pxa2xx_udc_irq,
IRQF_DISABLED, driver_name, dev);
if (retval != 0) {
- printk(KERN_ERR "%s: can't get irq %i, err %d\n",
- driver_name, IRQ_USB, retval);
+ printk(KERN_ERR "%s: can't get irq %d, err %d\n",
+ driver_name, irq, retval);
return -EBUSY;
}
dev->got_irq = 1;
@@ -2589,7 +2580,7 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
printk(KERN_ERR "%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_DISC_IRQ, retval);
lubbock_fail0:
- free_irq(IRQ_USB, dev);
+ free_irq(irq, dev);
return -EBUSY;
}
retval = request_irq(LUBBOCK_USB_IRQ,
@@ -2616,7 +2607,7 @@ lubbock_fail0:
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);
+ free_irq(irq, dev);
return -EBUSY;
}
}
@@ -2641,7 +2632,7 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
remove_proc_files();
if (dev->got_irq) {
- free_irq(IRQ_USB, dev);
+ free_irq(platform_get_irq(pdev, 0), dev);
dev->got_irq = 0;
}
#ifdef CONFIG_ARCH_LUBBOCK
@@ -2668,7 +2659,7 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
*
* For now, we punt and forcibly disconnect from the USB host when PXA
* enters any suspend state. While we're disconnected, we always disable
- * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states.
+ * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states.
* Boards without software pullup control shouldn't use those states.
* VBUS IRQs should probably be ignored so that the PXA device just acts
* "dead" to USB hosts until system resume.
@@ -2701,7 +2692,6 @@ static int pxa2xx_udc_resume(struct platform_device *dev)
/*-------------------------------------------------------------------------*/
static struct platform_driver udc_driver = {
- .probe = pxa2xx_udc_probe,
.shutdown = pxa2xx_udc_shutdown,
.remove = __exit_p(pxa2xx_udc_remove),
.suspend = pxa2xx_udc_suspend,
@@ -2715,7 +2705,7 @@ static struct platform_driver udc_driver = {
static int __init udc_init(void)
{
printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
- return platform_driver_register(&udc_driver);
+ return platform_driver_probe(&udc_driver, pxa2xx_udc_probe);
}
module_init(udc_init);
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
index 4c3c7259f019..397b149f3ca7 100644
--- a/drivers/usb/gadget/rndis.h
+++ b/drivers/usb/gadget/rndis.h
@@ -195,7 +195,7 @@ struct rndis_packet_msg_type
__le32 PerPacketInfoLength;
__le32 VcHandle;
__le32 Reserved;
-};
+} __attribute__ ((packed));
struct rndis_config_parameter
{
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index a2e58c86849f..2ff396bd180f 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -15,4 +15,3 @@ 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-fsl.h b/drivers/usb/host/ehci-fsl.h
index caac0d1967d0..f28736a917e4 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -31,7 +31,7 @@
#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
-#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */
-#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */
+#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */
+#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */
#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 1813b7cac294..f4d301bc83b9 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -136,6 +136,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* restore CMD_RUN, framelist size, and irq threshold */
ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ /* Some controller/firmware combinations need a delay during which
+ * they set up the port statuses. See Bugzilla #8190. */
+ mdelay(8);
+
/* manually resume the ports we suspended during bus_suspend() */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
deleted file mode 100644
index 32f7caf24747..000000000000
--- a/drivers/usb/host/hc_crisv10.c
+++ /dev/null
@@ -1,4550 +0,0 @@
-/*
- * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
- *
- * Copyright (c) 2002, 2003 Axis Communications AB.
- */
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/arch/svinto.h>
-
-#include <linux/usb.h>
-/* Ugly include because we don't live with the other host drivers. */
-#include <../drivers/usb/core/hcd.h>
-#include <../drivers/usb/core/usb.h>
-
-#include "hc_crisv10.h"
-
-#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
-#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
-#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
-
-static const char *usb_hcd_version = "$Revision: 1.2 $";
-
-#undef KERN_DEBUG
-#define KERN_DEBUG ""
-
-
-#undef USB_DEBUG_RH
-#undef USB_DEBUG_EPID
-#undef USB_DEBUG_SB
-#undef USB_DEBUG_DESC
-#undef USB_DEBUG_URB
-#undef USB_DEBUG_TRACE
-#undef USB_DEBUG_BULK
-#undef USB_DEBUG_CTRL
-#undef USB_DEBUG_INTR
-#undef USB_DEBUG_ISOC
-
-#ifdef USB_DEBUG_RH
-#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
-#else
-#define dbg_rh(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_EPID
-#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)
-#else
-#define dbg_epid(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_SB
-#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)
-#else
-#define dbg_sb(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_CTRL
-#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg)
-#else
-#define dbg_ctrl(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_BULK
-#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg)
-#else
-#define dbg_bulk(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_INTR
-#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg)
-#else
-#define dbg_intr(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_ISOC
-#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)
-#else
-#define dbg_isoc(format, arg...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_TRACE
-#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__))
-#define DBFEXIT (printk(": Exiting: %s\n", __FUNCTION__))
-#else
-#define DBFENTER do {} while (0)
-#define DBFEXIT do {} while (0)
-#endif
-
-#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)
-
-/*-------------------------------------------------------------------
- Virtual Root Hub
- -------------------------------------------------------------------*/
-
-static __u8 root_hub_dev_des[] =
-{
- 0x12, /* __u8 bLength; */
- 0x01, /* __u8 bDescriptorType; Device */
- 0x00, /* __le16 bcdUSB; v1.0 */
- 0x01,
- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
- 0x00, /* __u8 bDeviceSubClass; */
- 0x00, /* __u8 bDeviceProtocol; */
- 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
- 0x00, /* __le16 idVendor; */
- 0x00,
- 0x00, /* __le16 idProduct; */
- 0x00,
- 0x00, /* __le16 bcdDevice; */
- 0x00,
- 0x00, /* __u8 iManufacturer; */
- 0x02, /* __u8 iProduct; */
- 0x01, /* __u8 iSerialNumber; */
- 0x01 /* __u8 bNumConfigurations; */
-};
-
-/* Configuration descriptor */
-static __u8 root_hub_config_des[] =
-{
- 0x09, /* __u8 bLength; */
- 0x02, /* __u8 bDescriptorType; Configuration */
- 0x19, /* __le16 wTotalLength; */
- 0x00,
- 0x01, /* __u8 bNumInterfaces; */
- 0x01, /* __u8 bConfigurationValue; */
- 0x00, /* __u8 iConfiguration; */
- 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered */
- 0x00, /* __u8 MaxPower; */
-
- /* interface */
- 0x09, /* __u8 if_bLength; */
- 0x04, /* __u8 if_bDescriptorType; Interface */
- 0x00, /* __u8 if_bInterfaceNumber; */
- 0x00, /* __u8 if_bAlternateSetting; */
- 0x01, /* __u8 if_bNumEndpoints; */
- 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
- 0x00, /* __u8 if_bInterfaceSubClass; */
- 0x00, /* __u8 if_bInterfaceProtocol; */
- 0x00, /* __u8 if_iInterface; */
-
- /* endpoint */
- 0x07, /* __u8 ep_bLength; */
- 0x05, /* __u8 ep_bDescriptorType; Endpoint */
- 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
- 0x08, /* __le16 ep_wMaxPacketSize; 8 Bytes */
- 0x00,
- 0xff /* __u8 ep_bInterval; 255 ms */
-};
-
-static __u8 root_hub_hub_des[] =
-{
- 0x09, /* __u8 bLength; */
- 0x29, /* __u8 bDescriptorType; Hub-descriptor */
- 0x02, /* __u8 bNbrPorts; */
- 0x00, /* __u16 wHubCharacteristics; */
- 0x00,
- 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
- 0x00, /* __u8 bHubContrCurrent; 0 mA */
- 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
- 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
-};
-
-static DEFINE_TIMER(bulk_start_timer, NULL, 0, 0);
-static DEFINE_TIMER(bulk_eot_timer, NULL, 0, 0);
-
-/* We want the start timer to expire before the eot timer, because the former might start
- traffic, thus making it unnecessary for the latter to time out. */
-#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */
-#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */
-
-#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
-#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
-{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
-
-#define SLAB_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
-#define KMALLOC_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
-
-/* Most helpful debugging aid */
-#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
-
-/* Alternative assert define which stops after a failed assert. */
-/*
-#define assert(expr) \
-{ \
- if (!(expr)) { \
- err("assert failed at line %d",__LINE__); \
- while (1); \
- } \
-}
-*/
-
-
-/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically?
- To adjust it dynamically we would have to get an interrupt when we reach the end
- of the rx descriptor list, or when we get close to the end, and then allocate more
- descriptors. */
-
-#define NBR_OF_RX_DESC 512
-#define RX_DESC_BUF_SIZE 1024
-#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)
-
-/* The number of epids is, among other things, used for pre-allocating
- ctrl, bulk and isoc EP descriptors (one for each epid).
- Assumed to be > 1 when initiating the DMA lists. */
-#define NBR_OF_EPIDS 32
-
-/* Support interrupt traffic intervals up to 128 ms. */
-#define MAX_INTR_INTERVAL 128
-
-/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
- must be "invalid". By this we mean that we shouldn't care about epid attentions
- for this epid, or at least handle them differently from epid attentions for "valid"
- epids. This define determines which one to use (don't change it). */
-#define INVALID_EPID 31
-/* A special epid for the bulk dummys. */
-#define DUMMY_EPID 30
-
-/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */
-static __u32 epid_usage_bitmask;
-
-/* A bitfield to keep information on in/out traffic is needed to uniquely identify
- an endpoint on a device, since the most significant bit which indicates traffic
- direction is lacking in the ep_id field (ETRAX epids can handle both in and
- out traffic on endpoints that are otherwise identical). The USB framework, however,
- relies on them to be handled separately. For example, bulk IN and OUT urbs cannot
- be queued in the same list, since they would block each other. */
-static __u32 epid_out_traffic;
-
-/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line.
- Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */
-static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));
-static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
-
-/* Pointers into RxDescList. */
-static volatile USB_IN_Desc_t *myNextRxDesc;
-static volatile USB_IN_Desc_t *myLastRxDesc;
-static volatile USB_IN_Desc_t *myPrevRxDesc;
-
-/* EP descriptors must be 32-bit aligned. */
-static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set,
- causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which
- gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the
- EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors
- in each frame. */
-static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));
-
-static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
-static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));
-
-static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
-static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
-
-/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting
- this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0
- results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point
- it to this buffer. */
-static int zout_buffer[4] __attribute__ ((aligned (4)));
-
-/* Cache for allocating new EP and SB descriptors. */
-static struct kmem_cache *usb_desc_cache;
-
-/* Cache for the registers allocated in the top half. */
-static struct kmem_cache *top_half_reg_cache;
-
-/* Cache for the data allocated in the isoc descr top half. */
-static struct kmem_cache *isoc_compl_cache;
-
-static struct usb_bus *etrax_usb_bus;
-
-/* This is a circular (double-linked) list of the active urbs for each epid.
- The head is never removed, and new urbs are linked onto the list as
- urb_entry_t elements. Don't reference urb_list directly; use the wrapper
- functions instead. Note that working with these lists might require spinlock
- protection. */
-static struct list_head urb_list[NBR_OF_EPIDS];
-
-/* Read about the need and usage of this lock in submit_ctrl_urb. */
-static spinlock_t urb_list_lock;
-
-/* Used when unlinking asynchronously. */
-static struct list_head urb_unlink_list;
-
-/* for returning string descriptors in UTF-16LE */
-static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
-{
- int retval;
-
- for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
- *utf++ = *ascii++ & 0x7f;
- *utf++ = 0;
- }
- return retval;
-}
-
-static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
-{
- char buf [30];
-
- // assert (len > (2 * (sizeof (buf) + 1)));
- // assert (strlen (type) <= 8);
-
- // language ids
- if (id == 0) {
- *data++ = 4; *data++ = 3; /* 4 bytes data */
- *data++ = 0; *data++ = 0; /* some language id */
- return 4;
-
- // serial number
- } else if (id == 1) {
- sprintf (buf, "%x", serial);
-
- // product description
- } else if (id == 2) {
- sprintf (buf, "USB %s Root Hub", type);
-
- // id 3 == vendor description
-
- // unsupported IDs --> "stall"
- } else
- return 0;
-
- data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
- data [1] = 3;
- return data [0];
-}
-
-/* Wrappers around the list functions (include/linux/list.h). */
-
-static inline int urb_list_empty(int epid)
-{
- return list_empty(&urb_list[epid]);
-}
-
-/* Returns first urb for this epid, or NULL if list is empty. */
-static inline struct urb *urb_list_first(int epid)
-{
- struct urb *first_urb = 0;
-
- if (!urb_list_empty(epid)) {
- /* Get the first urb (i.e. head->next). */
- urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
- first_urb = urb_entry->urb;
- }
- return first_urb;
-}
-
-/* Adds an urb_entry last in the list for this epid. */
-static inline void urb_list_add(struct urb *urb, int epid)
-{
- urb_entry_t *urb_entry = kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
- assert(urb_entry);
-
- urb_entry->urb = urb;
- list_add_tail(&urb_entry->list, &urb_list[epid]);
-}
-
-/* Search through the list for an element that contains this urb. (The list
- is expected to be short and the one we are about to delete will often be
- the first in the list.) */
-static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid)
-{
- struct list_head *entry;
- struct list_head *tmp;
- urb_entry_t *urb_entry;
-
- list_for_each_safe(entry, tmp, &urb_list[epid]) {
- urb_entry = list_entry(entry, urb_entry_t, list);
- assert(urb_entry);
- assert(urb_entry->urb);
-
- if (urb_entry->urb == urb) {
- return urb_entry;
- }
- }
- return 0;
-}
-
-/* Delete an urb from the list. */
-static inline void urb_list_del(struct urb *urb, int epid)
-{
- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
- assert(urb_entry);
-
- /* Delete entry and free. */
- list_del(&urb_entry->list);
- kfree(urb_entry);
-}
-
-/* Move an urb to the end of the list. */
-static inline void urb_list_move_last(struct urb *urb, int epid)
-{
- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
- assert(urb_entry);
-
- list_move_tail(&urb_entry->list, &urb_list[epid]);
-}
-
-/* Get the next urb in the list. */
-static inline struct urb *urb_list_next(struct urb *urb, int epid)
-{
- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
-
- assert(urb_entry);
-
- if (urb_entry->list.next != &urb_list[epid]) {
- struct list_head *elem = urb_entry->list.next;
- urb_entry = list_entry(elem, urb_entry_t, list);
- return urb_entry->urb;
- } else {
- return NULL;
- }
-}
-
-
-
-/* For debug purposes only. */
-static inline void urb_list_dump(int epid)
-{
- struct list_head *entry;
- struct list_head *tmp;
- urb_entry_t *urb_entry;
- int i = 0;
-
- info("Dumping urb list for epid %d", epid);
-
- list_for_each_safe(entry, tmp, &urb_list[epid]) {
- urb_entry = list_entry(entry, urb_entry_t, list);
- info(" entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
- }
-}
-
-static void init_rx_buffers(void);
-static int etrax_rh_unlink_urb(struct urb *urb);
-static void etrax_rh_send_irq(struct urb *urb);
-static void etrax_rh_init_int_timer(struct urb *urb);
-static void etrax_rh_int_timer_do(unsigned long ptr);
-
-static int etrax_usb_setup_epid(struct urb *urb);
-static int etrax_usb_lookup_epid(struct urb *urb);
-static int etrax_usb_allocate_epid(void);
-static void etrax_usb_free_epid(int epid);
-
-static int etrax_remove_from_sb_list(struct urb *urb);
-
-static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size,
- unsigned mem_flags, dma_addr_t *dma);
-static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma);
-
-static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid);
-static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid);
-
-static int etrax_usb_submit_bulk_urb(struct urb *urb);
-static int etrax_usb_submit_ctrl_urb(struct urb *urb);
-static int etrax_usb_submit_intr_urb(struct urb *urb);
-static int etrax_usb_submit_isoc_urb(struct urb *urb);
-
-static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags);
-static int etrax_usb_unlink_urb(struct urb *urb, int status);
-static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
-
-static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc);
-static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc);
-static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc);
-static void etrax_usb_hc_interrupt_bottom_half(void *data);
-
-static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data);
-
-
-/* The following is a list of interrupt handlers for the host controller interrupts we use.
- They are called from etrax_usb_hc_interrupt_bottom_half. */
-static void etrax_usb_hc_isoc_eof_interrupt(void);
-static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced);
-static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg);
-static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg);
-static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg);
-
-static int etrax_rh_submit_urb (struct urb *urb);
-
-/* Forward declaration needed because they are used in the rx interrupt routine. */
-static void etrax_usb_complete_urb(struct urb *urb, int status);
-static void etrax_usb_complete_bulk_urb(struct urb *urb, int status);
-static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status);
-static void etrax_usb_complete_intr_urb(struct urb *urb, int status);
-static void etrax_usb_complete_isoc_urb(struct urb *urb, int status);
-
-static int etrax_usb_hc_init(void);
-static void etrax_usb_hc_cleanup(void);
-
-static struct usb_operations etrax_usb_device_operations =
-{
- .get_frame_number = etrax_usb_get_frame_number,
- .submit_urb = etrax_usb_submit_urb,
- .unlink_urb = etrax_usb_unlink_urb,
- .buffer_alloc = etrax_usb_buffer_alloc,
- .buffer_free = etrax_usb_buffer_free
-};
-
-/* Note that these functions are always available in their "__" variants, for use in
- error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/
- USB_DEBUG_URB macros. */
-static void __dump_urb(struct urb* purb)
-{
- printk("\nurb :0x%08lx\n", (unsigned long)purb);
- printk("dev :0x%08lx\n", (unsigned long)purb->dev);
- printk("pipe :0x%08x\n", purb->pipe);
- printk("status :%d\n", purb->status);
- printk("transfer_flags :0x%08x\n", purb->transfer_flags);
- printk("transfer_buffer :0x%08lx\n", (unsigned long)purb->transfer_buffer);
- printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
- printk("actual_length :%d\n", purb->actual_length);
- printk("setup_packet :0x%08lx\n", (unsigned long)purb->setup_packet);
- printk("start_frame :%d\n", purb->start_frame);
- printk("number_of_packets :%d\n", purb->number_of_packets);
- printk("interval :%d\n", purb->interval);
- printk("error_count :%d\n", purb->error_count);
- printk("context :0x%08lx\n", (unsigned long)purb->context);
- printk("complete :0x%08lx\n\n", (unsigned long)purb->complete);
-}
-
-static void __dump_in_desc(volatile USB_IN_Desc_t *in)
-{
- printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in);
- printk(" sw_len : 0x%04x (%d)\n", in->sw_len, in->sw_len);
- printk(" command : 0x%04x\n", in->command);
- printk(" next : 0x%08lx\n", in->next);
- printk(" buf : 0x%08lx\n", in->buf);
- printk(" hw_len : 0x%04x (%d)\n", in->hw_len, in->hw_len);
- printk(" status : 0x%04x\n\n", in->status);
-}
-
-static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
-{
- char tt = (sb->command & 0x30) >> 4;
- char *tt_string;
-
- switch (tt) {
- case 0:
- tt_string = "zout";
- break;
- case 1:
- tt_string = "in";
- break;
- case 2:
- tt_string = "out";
- break;
- case 3:
- tt_string = "setup";
- break;
- default:
- tt_string = "unknown (weird)";
- }
-
- printk("\n USB_SB_Desc at 0x%08lx\n", (unsigned long)sb);
- printk(" command : 0x%04x\n", sb->command);
- printk(" rem : %d\n", (sb->command & 0x3f00) >> 8);
- printk(" full : %d\n", (sb->command & 0x40) >> 6);
- printk(" tt : %d (%s)\n", tt, tt_string);
- printk(" intr : %d\n", (sb->command & 0x8) >> 3);
- printk(" eot : %d\n", (sb->command & 0x2) >> 1);
- printk(" eol : %d\n", sb->command & 0x1);
- printk(" sw_len : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
- printk(" next : 0x%08lx\n", sb->next);
- printk(" buf : 0x%08lx\n\n", sb->buf);
-}
-
-
-static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
-{
- printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
- printk(" command : 0x%04x\n", ep->command);
- printk(" ep_id : %d\n", (ep->command & 0x1f00) >> 8);
- printk(" enable : %d\n", (ep->command & 0x10) >> 4);
- printk(" intr : %d\n", (ep->command & 0x8) >> 3);
- printk(" eof : %d\n", (ep->command & 0x2) >> 1);
- printk(" eol : %d\n", ep->command & 0x1);
- printk(" hw_len : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
- printk(" next : 0x%08lx\n", ep->next);
- printk(" sub : 0x%08lx\n\n", ep->sub);
-}
-
-static inline void __dump_ep_list(int pipe_type)
-{
- volatile USB_EP_Desc_t *ep;
- volatile USB_EP_Desc_t *first_ep;
- volatile USB_SB_Desc_t *sb;
-
- switch (pipe_type)
- {
- case PIPE_BULK:
- first_ep = &TxBulkEPList[0];
- break;
- case PIPE_CONTROL:
- first_ep = &TxCtrlEPList[0];
- break;
- case PIPE_INTERRUPT:
- first_ep = &TxIntrEPList[0];
- break;
- case PIPE_ISOCHRONOUS:
- first_ep = &TxIsocEPList[0];
- break;
- default:
- warn("Cannot dump unknown traffic type");
- return;
- }
- ep = first_ep;
-
- printk("\n\nDumping EP list...\n\n");
-
- do {
- __dump_ep_desc(ep);
- /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
- sb = ep->sub ? phys_to_virt(ep->sub) : 0;
- while (sb) {
- __dump_sb_desc(sb);
- sb = sb->next ? phys_to_virt(sb->next) : 0;
- }
- ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
-
- } while (ep != first_ep);
-}
-
-static inline void __dump_ept_data(int epid)
-{
- unsigned long flags;
- __u32 r_usb_ept_data;
-
- if (epid < 0 || epid > 31) {
- printk("Cannot dump ept data for invalid epid %d\n", epid);
- return;
- }
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- r_usb_ept_data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid);
- if (r_usb_ept_data == 0) {
- /* No need for more detailed printing. */
- return;
- }
- printk(" valid : %d\n", (r_usb_ept_data & 0x80000000) >> 31);
- printk(" hold : %d\n", (r_usb_ept_data & 0x40000000) >> 30);
- printk(" error_count_in : %d\n", (r_usb_ept_data & 0x30000000) >> 28);
- printk(" t_in : %d\n", (r_usb_ept_data & 0x08000000) >> 27);
- printk(" low_speed : %d\n", (r_usb_ept_data & 0x04000000) >> 26);
- printk(" port : %d\n", (r_usb_ept_data & 0x03000000) >> 24);
- printk(" error_code : %d\n", (r_usb_ept_data & 0x00c00000) >> 22);
- printk(" t_out : %d\n", (r_usb_ept_data & 0x00200000) >> 21);
- printk(" error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19);
- printk(" max_len : %d\n", (r_usb_ept_data & 0x0003f800) >> 11);
- printk(" ep : %d\n", (r_usb_ept_data & 0x00000780) >> 7);
- printk(" dev : %d\n", (r_usb_ept_data & 0x0000003f));
-}
-
-static inline void __dump_ept_data_list(void)
-{
- int i;
-
- printk("Dumping the whole R_USB_EPT_DATA list\n");
-
- for (i = 0; i < 32; i++) {
- __dump_ept_data(i);
- }
-}
-#ifdef USB_DEBUG_DESC
-#define dump_in_desc(...) __dump_in_desc(...)
-#define dump_sb_desc(...) __dump_sb_desc(...)
-#define dump_ep_desc(...) __dump_ep_desc(...)
-#else
-#define dump_in_desc(...) do {} while (0)
-#define dump_sb_desc(...) do {} while (0)
-#define dump_ep_desc(...) do {} while (0)
-#endif
-
-#ifdef USB_DEBUG_URB
-#define dump_urb(x) __dump_urb(x)
-#else
-#define dump_urb(x) do {} while (0)
-#endif
-
-static void init_rx_buffers(void)
-{
- int i;
-
- DBFENTER;
-
- for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
- RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
- RxDescList[i].command = 0;
- RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
- RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
- RxDescList[i].hw_len = 0;
- RxDescList[i].status = 0;
-
- /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
- for the relevant fields.) */
- prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
-
- }
-
- RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
- RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes);
- RxDescList[i].next = virt_to_phys(&RxDescList[0]);
- RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
- RxDescList[i].hw_len = 0;
- RxDescList[i].status = 0;
-
- myNextRxDesc = &RxDescList[0];
- myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
- myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
-
- *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc);
- *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-static void init_tx_bulk_ep(void)
-{
- int i;
-
- DBFENTER;
-
- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
- CHECK_ALIGN(&TxBulkEPList[i]);
- TxBulkEPList[i].hw_len = 0;
- TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
- TxBulkEPList[i].sub = 0;
- TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]);
-
- /* Initiate two EPs, disabled and with the eol flag set. No need for any
- preserved epid. */
-
- /* The first one has the intr flag set so we get an interrupt when the DMA
- channel is about to become disabled. */
- CHECK_ALIGN(&TxBulkDummyEPList[i][0]);
- TxBulkDummyEPList[i][0].hw_len = 0;
- TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
- IO_STATE(USB_EP_command, eol, yes) |
- IO_STATE(USB_EP_command, intr, yes));
- TxBulkDummyEPList[i][0].sub = 0;
- TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]);
-
- /* The second one. */
- CHECK_ALIGN(&TxBulkDummyEPList[i][1]);
- TxBulkDummyEPList[i][1].hw_len = 0;
- TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
- IO_STATE(USB_EP_command, eol, yes));
- TxBulkDummyEPList[i][1].sub = 0;
- /* The last dummy's next pointer is the same as the current EP's next pointer. */
- TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]);
- }
-
- /* Configure the last one. */
- CHECK_ALIGN(&TxBulkEPList[i]);
- TxBulkEPList[i].hw_len = 0;
- TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
- IO_FIELD(USB_EP_command, epid, i));
- TxBulkEPList[i].sub = 0;
- TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
-
- /* No need configuring dummy EPs for the last one as it will never be used for
- bulk traffic (i == INVALD_EPID at this point). */
-
- /* Set up to start on the last EP so we will enable it when inserting traffic
- for the first time (imitating the situation where the DMA has stopped
- because there was no more traffic). */
- *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]);
- /* No point in starting the bulk channel yet.
- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */
- DBFEXIT;
-}
-
-static void init_tx_ctrl_ep(void)
-{
- int i;
-
- DBFENTER;
-
- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
- CHECK_ALIGN(&TxCtrlEPList[i]);
- TxCtrlEPList[i].hw_len = 0;
- TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
- TxCtrlEPList[i].sub = 0;
- TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
- }
-
- CHECK_ALIGN(&TxCtrlEPList[i]);
- TxCtrlEPList[i].hw_len = 0;
- TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
- IO_FIELD(USB_EP_command, epid, i));
-
- TxCtrlEPList[i].sub = 0;
- TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
-
- *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
- *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-
-static void init_tx_intr_ep(void)
-{
- int i;
-
- DBFENTER;
-
- /* Read comment at zout_buffer declaration for an explanation to this. */
- TxIntrSB_zout.sw_len = 1;
- TxIntrSB_zout.next = 0;
- TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
- TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, zout) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
- CHECK_ALIGN(&TxIntrEPList[i]);
- TxIntrEPList[i].hw_len = 0;
- TxIntrEPList[i].command =
- (IO_STATE(USB_EP_command, eof, yes) |
- IO_STATE(USB_EP_command, enable, yes) |
- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
- TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
- TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
- }
-
- CHECK_ALIGN(&TxIntrEPList[i]);
- TxIntrEPList[i].hw_len = 0;
- TxIntrEPList[i].command =
- (IO_STATE(USB_EP_command, eof, yes) |
- IO_STATE(USB_EP_command, eol, yes) |
- IO_STATE(USB_EP_command, enable, yes) |
- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
- TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
- TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]);
-
- *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
- *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
- DBFEXIT;
-}
-
-static void init_tx_isoc_ep(void)
-{
- int i;
-
- DBFENTER;
-
- /* Read comment at zout_buffer declaration for an explanation to this. */
- TxIsocSB_zout.sw_len = 1;
- TxIsocSB_zout.next = 0;
- TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
- TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, zout) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- /* The last isochronous EP descriptor is a dummy. */
-
- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
- CHECK_ALIGN(&TxIsocEPList[i]);
- TxIsocEPList[i].hw_len = 0;
- TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
- TxIsocEPList[i].sub = 0;
- TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
- }
-
- CHECK_ALIGN(&TxIsocEPList[i]);
- TxIsocEPList[i].hw_len = 0;
-
- /* Must enable the last EP descr to get eof interrupt. */
- TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
- IO_STATE(USB_EP_command, eof, yes) |
- IO_STATE(USB_EP_command, eol, yes) |
- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
- TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
- TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
-
- *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]);
- *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-static void etrax_usb_unlink_intr_urb(struct urb *urb)
-{
- volatile USB_EP_Desc_t *first_ep; /* First EP in the list. */
- volatile USB_EP_Desc_t *curr_ep; /* Current EP, the iterator. */
- volatile USB_EP_Desc_t *next_ep; /* The EP after current. */
- volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */
-
- int epid;
-
- /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
-
- DBFENTER;
-
- epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid;
-
- first_ep = &TxIntrEPList[0];
- curr_ep = first_ep;
-
-
- /* Note that this loop removes all EP descriptors with this epid. This assumes
- that all EP descriptors belong to the one and only urb for this epid. */
-
- do {
- next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
-
- if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
-
- dbg_intr("Found EP to unlink for epid %d", epid);
-
- /* This is the one we should unlink. */
- unlink_ep = next_ep;
-
- /* Actually unlink the EP from the DMA list. */
- curr_ep->next = unlink_ep->next;
-
- /* Wait until the DMA is no longer at this descriptor. */
- while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
-
- /* Now we are free to remove it and its SB descriptor.
- Note that it is assumed here that there is only one sb in the
- sb list for this ep. */
- kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
- kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
- }
-
- curr_ep = phys_to_virt(curr_ep->next);
-
- } while (curr_ep != first_ep);
- urb->hcpriv = NULL;
-}
-
-void etrax_usb_do_intr_recover(int epid)
-{
- USB_EP_Desc_t *first_ep, *tmp_ep;
-
- DBFENTER;
-
- first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
- tmp_ep = first_ep;
-
- /* What this does is simply to walk the list of interrupt
- ep descriptors and enable those that are disabled. */
-
- do {
- if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
- !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
- tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
- }
-
- tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
-
- } while (tmp_ep != first_ep);
-
-
- DBFEXIT;
-}
-
-static int etrax_rh_unlink_urb (struct urb *urb)
-{
- etrax_hc_t *hc;
-
- DBFENTER;
-
- hc = urb->dev->bus->hcpriv;
-
- if (hc->rh.urb == urb) {
- hc->rh.send = 0;
- del_timer(&hc->rh.rh_int_timer);
- }
-
- DBFEXIT;
- return 0;
-}
-
-static void etrax_rh_send_irq(struct urb *urb)
-{
- __u16 data = 0;
- etrax_hc_t *hc = urb->dev->bus->hcpriv;
- DBFENTER;
-
-/*
- dbg_rh("R_USB_FM_NUMBER : 0x%08X", *R_USB_FM_NUMBER);
- dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
-*/
-
- data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0;
- data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
-
- *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
- /* FIXME: Why is actual_length set to 1 when data is 2 bytes?
- Since only 1 byte is used, why not declare data as __u8? */
- urb->actual_length = 1;
- urb->status = 0;
-
- if (hc->rh.send && urb->complete) {
- dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1);
- dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2);
-
- urb->complete(urb, NULL);
- }
-
- DBFEXIT;
-}
-
-static void etrax_rh_init_int_timer(struct urb *urb)
-{
- etrax_hc_t *hc;
-
- DBFENTER;
-
- hc = urb->dev->bus->hcpriv;
- hc->rh.interval = urb->interval;
- init_timer(&hc->rh.rh_int_timer);
- hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
- hc->rh.rh_int_timer.data = (unsigned long)urb;
- /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
- to 0, and the rest to the nearest lower 10 ms. */
- hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
- add_timer(&hc->rh.rh_int_timer);
-
- DBFEXIT;
-}
-
-static void etrax_rh_int_timer_do(unsigned long ptr)
-{
- struct urb *urb;
- etrax_hc_t *hc;
-
- DBFENTER;
-
- urb = (struct urb*)ptr;
- hc = urb->dev->bus->hcpriv;
-
- if (hc->rh.send) {
- etrax_rh_send_irq(urb);
- }
-
- DBFEXIT;
-}
-
-static int etrax_usb_setup_epid(struct urb *urb)
-{
- int epid;
- char devnum, endpoint, out_traffic, slow;
- int maxlen;
- unsigned long flags;
-
- DBFENTER;
-
- epid = etrax_usb_lookup_epid(urb);
- if ((epid != -1)){
- /* An epid that fits this urb has been found. */
- DBFEXIT;
- return epid;
- }
-
- /* We must find and initiate a new epid for this urb. */
- epid = etrax_usb_allocate_epid();
-
- if (epid == -1) {
- /* Failed to allocate a new epid. */
- DBFEXIT;
- return epid;
- }
-
- /* We now have a new epid to use. Initiate it. */
- set_bit(epid, (void *)&epid_usage_bitmask);
-
- devnum = usb_pipedevice(urb->pipe);
- endpoint = usb_pipeendpoint(urb->pipe);
- slow = usb_pipeslow(urb->pipe);
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
- /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
- out_traffic = 1;
- } else {
- out_traffic = usb_pipeout(urb->pipe);
- }
-
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
-
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- *R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) |
- /* FIXME: Change any to the actual port? */
- IO_STATE(R_USB_EPT_DATA_ISO, port, any) |
- IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) |
- IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) |
- IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum);
- } else {
- *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
- IO_FIELD(R_USB_EPT_DATA, low_speed, slow) |
- /* FIXME: Change any to the actual port? */
- IO_STATE(R_USB_EPT_DATA, port, any) |
- IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) |
- IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
- IO_FIELD(R_USB_EPT_DATA, dev, devnum);
- }
-
- restore_flags(flags);
-
- if (out_traffic) {
- set_bit(epid, (void *)&epid_out_traffic);
- } else {
- clear_bit(epid, (void *)&epid_out_traffic);
- }
-
- dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
- epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
-
- DBFEXIT;
- return epid;
-}
-
-static void etrax_usb_free_epid(int epid)
-{
- unsigned long flags;
-
- DBFENTER;
-
- if (!test_bit(epid, (void *)&epid_usage_bitmask)) {
- warn("Trying to free unused epid %d", epid);
- DBFEXIT;
- return;
- }
-
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
- /* This will, among other things, set the valid field to 0. */
- *R_USB_EPT_DATA = 0;
- restore_flags(flags);
-
- clear_bit(epid, (void *)&epid_usage_bitmask);
-
-
- dbg_epid("Freed epid %d", epid);
-
- DBFEXIT;
-}
-
-static int etrax_usb_lookup_epid(struct urb *urb)
-{
- int i;
- __u32 data;
- char devnum, endpoint, slow, out_traffic;
- int maxlen;
- unsigned long flags;
-
- DBFENTER;
-
- devnum = usb_pipedevice(urb->pipe);
- endpoint = usb_pipeendpoint(urb->pipe);
- slow = usb_pipeslow(urb->pipe);
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
- /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
- out_traffic = 1;
- } else {
- out_traffic = usb_pipeout(urb->pipe);
- }
-
- /* Step through att epids. */
- for (i = 0; i < NBR_OF_EPIDS; i++) {
- if (test_bit(i, (void *)&epid_usage_bitmask) &&
- test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
- nop();
-
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- data = *R_USB_EPT_DATA_ISO;
- restore_flags(flags);
-
- if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
- (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
- (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
- (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
- dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
- i, devnum, endpoint, out_traffic ? "OUT" : "IN");
- DBFEXIT;
- return i;
- }
- } else {
- data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
- (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
- (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
- (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
- (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {
- dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
- i, devnum, endpoint, out_traffic ? "OUT" : "IN");
- DBFEXIT;
- return i;
- }
- }
- }
- }
-
- DBFEXIT;
- return -1;
-}
-
-static int etrax_usb_allocate_epid(void)
-{
- int i;
-
- DBFENTER;
-
- for (i = 0; i < NBR_OF_EPIDS; i++) {
- if (!test_bit(i, (void *)&epid_usage_bitmask)) {
- dbg_epid("Found free epid %d", i);
- DBFEXIT;
- return i;
- }
- }
-
- dbg_epid("Found no free epids");
- DBFEXIT;
- return -1;
-}
-
-static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags)
-{
- etrax_hc_t *hc;
- int ret = -EINVAL;
-
- DBFENTER;
-
- if (!urb->dev || !urb->dev->bus) {
- return -ENODEV;
- }
- if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) {
- info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe);
- return -EMSGSIZE;
- }
-
- if (urb->timeout) {
- /* FIXME. */
- warn("urb->timeout specified, ignoring.");
- }
-
- hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
-
- if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
- /* This request is for the Virtual Root Hub. */
- ret = etrax_rh_submit_urb(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
- ret = etrax_usb_submit_bulk_urb(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
- ret = etrax_usb_submit_ctrl_urb(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
- int bustime;
-
- if (urb->bandwidth == 0) {
- bustime = usb_check_bandwidth(urb->dev, urb);
- if (bustime < 0) {
- ret = bustime;
- } else {
- ret = etrax_usb_submit_intr_urb(urb);
- if (ret == 0)
- usb_claim_bandwidth(urb->dev, urb, bustime, 0);
- }
- } else {
- /* Bandwidth already set. */
- ret = etrax_usb_submit_intr_urb(urb);
- }
-
- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- int bustime;
-
- if (urb->bandwidth == 0) {
- bustime = usb_check_bandwidth(urb->dev, urb);
- if (bustime < 0) {
- ret = bustime;
- } else {
- ret = etrax_usb_submit_isoc_urb(urb);
- if (ret == 0)
- usb_claim_bandwidth(urb->dev, urb, bustime, 0);
- }
- } else {
- /* Bandwidth already set. */
- ret = etrax_usb_submit_isoc_urb(urb);
- }
- }
-
- DBFEXIT;
-
- if (ret != 0)
- printk("Submit URB error %d\n", ret);
-
- return ret;
-}
-
-static int etrax_usb_unlink_urb(struct urb *urb, int status)
-{
- etrax_hc_t *hc;
- etrax_urb_priv_t *urb_priv;
- int epid;
- unsigned int flags;
-
- DBFENTER;
-
- if (!urb) {
- return -EINVAL;
- }
-
- /* Disable interrupts here since a descriptor interrupt for the isoc epid
- will modify the sb list. This could possibly be done more granular, but
- unlink_urb should not be used frequently anyway.
- */
-
- save_flags(flags);
- cli();
-
- if (!urb->dev || !urb->dev->bus) {
- restore_flags(flags);
- return -ENODEV;
- }
- if (!urb->hcpriv) {
- /* This happens if a device driver calls unlink on an urb that
- was never submitted (lazy driver) or if the urb was completed
- while unlink was being called. */
- restore_flags(flags);
- return 0;
- }
- if (urb->transfer_flags & URB_ASYNC_UNLINK) {
- /* FIXME. */
- /* If URB_ASYNC_UNLINK is set:
- unlink
- move to a separate urb list
- call complete at next sof with ECONNRESET
-
- If not:
- wait 1 ms
- unlink
- call complete with ENOENT
- */
- warn("URB_ASYNC_UNLINK set, ignoring.");
- }
-
- /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
- but that doesn't work for interrupt and isochronous traffic since they are completed
- repeatedly, and urb->status is set then. That may in itself be a bug though. */
-
- hc = urb->dev->bus->hcpriv;
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- epid = urb_priv->epid;
-
- /* Set the urb status (synchronous unlink). */
- urb->status = -ENOENT;
- urb_priv->urb_state = UNLINK;
-
- if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
- int ret;
- ret = etrax_rh_unlink_urb(urb);
- DBFEXIT;
- restore_flags(flags);
- return ret;
-
- } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
- dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
-
- if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- /* The EP was enabled, disable it and wait. */
- TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
- /* Ah, the luxury of busy-wait. */
- while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
- }
- /* Kicking dummy list out of the party. */
- TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
- dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
-
- if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- /* The EP was enabled, disable it and wait. */
- TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
- /* Ah, the luxury of busy-wait. */
- while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
- }
-
- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-
- dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
-
- /* Separate function because it's a tad more complicated. */
- etrax_usb_unlink_intr_urb(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
- dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
-
- if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- /* The EP was enabled, disable it and wait. */
- TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
- /* Ah, the luxury of busy-wait. */
- while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
- }
- }
-
- /* Note that we need to remove the urb from the urb list *before* removing its SB
- descriptors. (This means that the isoc eof handler might get a null urb when we
- are unlinking the last urb.) */
-
- if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
- urb_list_del(urb, epid);
- TxBulkEPList[epid].sub = 0;
- etrax_remove_from_sb_list(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
- urb_list_del(urb, epid);
- TxCtrlEPList[epid].sub = 0;
- etrax_remove_from_sb_list(urb);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-
- urb_list_del(urb, epid);
- /* Sanity check (should never happen). */
- assert(urb_list_empty(epid));
-
- /* Release allocated bandwidth. */
- usb_release_bandwidth(urb->dev, urb, 0);
-
- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
- if (usb_pipeout(urb->pipe)) {
-
- USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb;
-
- if (__urb_list_entry(urb, epid)) {
-
- urb_list_del(urb, epid);
- iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
- prev_sb = 0;
- while (iter_sb && (iter_sb != urb_priv->first_sb)) {
- prev_sb = iter_sb;
- iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
- }
-
- if (iter_sb == 0) {
- /* Unlink of the URB currently being transmitted. */
- prev_sb = 0;
- iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
- }
-
- while (iter_sb && (iter_sb != urb_priv->last_sb)) {
- iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
- }
- if (iter_sb) {
- next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
- } else {
- /* This should only happen if the DMA has completed
- processing the SB list for this EP while interrupts
- are disabled. */
- dbg_isoc("Isoc urb not found, already sent?");
- next_sb = 0;
- }
- if (prev_sb) {
- prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0;
- } else {
- TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0;
- }
-
- etrax_remove_from_sb_list(urb);
- if (urb_list_empty(epid)) {
- TxIsocEPList[epid].sub = 0;
- dbg_isoc("Last isoc out urb epid %d", epid);
- } else if (next_sb || prev_sb) {
- dbg_isoc("Re-enable isoc out epid %d", epid);
-
- TxIsocEPList[epid].hw_len = 0;
- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
- } else {
- TxIsocEPList[epid].sub = 0;
- dbg_isoc("URB list non-empty and no SB list, EP disabled");
- }
- } else {
- dbg_isoc("Urb 0x%p not found, completed already?", urb);
- }
- } else {
-
- urb_list_del(urb, epid);
-
- /* For in traffic there is only one SB descriptor for each EP even
- though there may be several urbs (all urbs point at the same SB). */
- if (urb_list_empty(epid)) {
- /* No more urbs, remove the SB. */
- TxIsocEPList[epid].sub = 0;
- etrax_remove_from_sb_list(urb);
- } else {
- TxIsocEPList[epid].hw_len = 0;
- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
- }
- }
- /* Release allocated bandwidth. */
- usb_release_bandwidth(urb->dev, urb, 1);
- }
- /* Free the epid if urb list is empty. */
- if (urb_list_empty(epid)) {
- etrax_usb_free_epid(epid);
- }
- restore_flags(flags);
-
- /* Must be done before calling completion handler. */
- kfree(urb_priv);
- urb->hcpriv = 0;
-
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- DBFEXIT;
- return 0;
-}
-
-static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
-{
- DBFENTER;
- DBFEXIT;
- return (*R_USB_FM_NUMBER & 0x7ff);
-}
-
-static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc)
-{
- DBFENTER;
-
- /* This interrupt handler could be used when unlinking EP descriptors. */
-
- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
- USB_EP_Desc_t *ep;
-
- //dbg_bulk("dma8_sub0_descr (BULK) intr.");
-
- /* It should be safe clearing the interrupt here, since we don't expect to get a new
- one until we restart the bulk channel. */
- *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
-
- /* Wait while the DMA is running (though we don't expect it to be). */
- while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd));
-
- /* Advance the DMA to the next EP descriptor. */
- ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
-
- //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep);
-
- /* ep->next is already a physical address; no need for a virt_to_phys. */
- *R_DMA_CH8_SUB0_EP = ep->next;
-
- /* Start the DMA bulk channel again. */
- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
- }
- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
- struct urb *urb;
- int epid;
- etrax_urb_priv_t *urb_priv;
- unsigned long int flags;
-
- dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
- *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
-
- /* The complete callback gets called so we cli. */
- save_flags(flags);
- cli();
-
- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
- if ((TxCtrlEPList[epid].sub == 0) ||
- (epid == DUMMY_EPID) ||
- (epid == INVALID_EPID)) {
- /* Nothing here to see. */
- continue;
- }
-
- /* Get the first urb (if any). */
- urb = urb_list_first(epid);
-
- if (urb) {
-
- /* Sanity check. */
- assert(usb_pipetype(urb->pipe) == PIPE_CONTROL);
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) {
- assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
-
- etrax_usb_complete_urb(urb, 0);
- }
- }
- }
- restore_flags(flags);
- }
- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
- dbg_intr("dma8_sub2_descr (INTR) intr.");
- *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
- }
- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
- struct urb *urb;
- int epid;
- int epid_done;
- etrax_urb_priv_t *urb_priv;
- USB_SB_Desc_t *sb_desc;
-
- usb_isoc_complete_data_t *comp_data = NULL;
-
- /* One or more isoc out transfers are done. */
- dbg_isoc("dma8_sub3_descr (ISOC) intr.");
-
- /* For each isoc out EP search for the first sb_desc with the intr flag
- set. This descriptor must be the last packet from an URB. Then
- traverse the URB list for the EP until the URB with urb_priv->last_sb
- matching the intr-marked sb_desc is found. All URBs before this have
- been sent.
- */
-
- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
- /* Skip past epids with no SB lists, epids used for in traffic,
- and special (dummy, invalid) epids. */
- if ((TxIsocEPList[epid].sub == 0) ||
- (test_bit(epid, (void *)&epid_out_traffic) == 0) ||
- (epid == DUMMY_EPID) ||
- (epid == INVALID_EPID)) {
- /* Nothing here to see. */
- continue;
- }
- sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
-
- /* Find the last descriptor of the currently active URB for this ep.
- This is the first descriptor in the sub list marked for a descriptor
- interrupt. */
- while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) {
- sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0;
- }
- assert(sb_desc);
-
- dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p",
- epid,
- phys_to_virt(TxIsocEPList[epid].sub),
- sb_desc);
-
- epid_done = 0;
-
- /* Get the first urb (if any). */
- urb = urb_list_first(epid);
- assert(urb);
-
- while (urb && !epid_done) {
-
- /* Sanity check. */
- assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
-
- if (!usb_pipeout(urb->pipe)) {
- /* descr interrupts are generated only for out pipes. */
- epid_done = 1;
- continue;
- }
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if (sb_desc != urb_priv->last_sb) {
-
- /* This urb has been sent. */
- dbg_isoc("out URB 0x%p sent", urb);
-
- urb_priv->urb_state = TRANSFER_DONE;
-
- } else if ((sb_desc == urb_priv->last_sb) &&
- !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
-
- assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes));
- assert(sb_desc->next == 0);
-
- dbg_isoc("out URB 0x%p last in list, epid disabled", urb);
- TxIsocEPList[epid].sub = 0;
- TxIsocEPList[epid].hw_len = 0;
- urb_priv->urb_state = TRANSFER_DONE;
-
- epid_done = 1;
-
- } else {
- epid_done = 1;
- }
- if (!epid_done) {
- urb = urb_list_next(urb, epid);
- }
- }
-
- }
-
- *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
-
- comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, GFP_ATOMIC);
- assert(comp_data != NULL);
-
- INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data);
- schedule_work(&comp_data->usb_bh);
- }
-
- DBFEXIT;
- return IRQ_HANDLED;
-}
-
-static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data)
-{
- usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data;
-
- struct urb *urb;
- int epid;
- int epid_done;
- etrax_urb_priv_t *urb_priv;
-
- DBFENTER;
-
- dbg_isoc("dma8_sub3_descr (ISOC) bottom half.");
-
- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- epid_done = 0;
-
- /* The descriptor interrupt handler has marked all transmitted isoch. out
- URBs with TRANSFER_DONE. Now we traverse all epids and for all that
- have isoch. out traffic traverse its URB list and complete the
- transmitted URB.
- */
-
- while (!epid_done) {
-
- /* Get the first urb (if any). */
- urb = urb_list_first(epid);
- if (urb == 0) {
- epid_done = 1;
- continue;
- }
-
- if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
- epid_done = 1;
- continue;
- }
-
- if (!usb_pipeout(urb->pipe)) {
- /* descr interrupts are generated only for out pipes. */
- epid_done = 1;
- continue;
- }
-
- dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub);
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if (urb_priv->urb_state == TRANSFER_DONE) {
- int i;
- struct usb_iso_packet_descriptor *packet;
-
- /* This urb has been sent. */
- dbg_isoc("Completing isoc out URB 0x%p", urb);
-
- for (i = 0; i < urb->number_of_packets; i++) {
- packet = &urb->iso_frame_desc[i];
- packet->status = 0;
- packet->actual_length = packet->length;
- }
-
- etrax_usb_complete_isoc_urb(urb, 0);
-
- if (urb_list_empty(epid)) {
- etrax_usb_free_epid(epid);
- epid_done = 1;
- }
- } else {
- epid_done = 1;
- }
- }
- restore_flags(flags);
-
- }
- kmem_cache_free(isoc_compl_cache, comp_data);
-
- DBFEXIT;
-}
-
-
-
-static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc)
-{
- struct urb *urb;
- etrax_urb_priv_t *urb_priv;
- int epid = 0;
- unsigned long flags;
-
- /* Isoc diagnostics. */
- static int curr_fm = 0;
- static int prev_fm = 0;
-
- DBFENTER;
-
- /* Clear this interrupt. */
- *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
-
- /* Note that this while loop assumes that all packets span only
- one rx descriptor. */
-
- /* The reason we cli here is that we call the driver's callback functions. */
- save_flags(flags);
- cli();
-
- while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
-
- epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
- urb = urb_list_first(epid);
-
- //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
-
- if (!urb) {
- err("No urb for epid %d in rx interrupt", epid);
- __dump_ept_data(epid);
- goto skip_out;
- }
-
- /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
- ctrl pipes are not. */
-
- if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
- __u32 r_usb_ept_data;
- int no_error = 0;
-
- assert(test_bit(epid, (void *)&epid_usage_bitmask));
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- r_usb_ept_data = *R_USB_EPT_DATA_ISO;
-
- if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) &&
- (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) &&
- (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) {
- /* Not an error, just a failure to receive an expected iso
- in packet in this frame. This is not documented
- in the designers reference.
- */
- no_error++;
- } else {
- warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data);
- }
- } else {
- r_usb_ept_data = *R_USB_EPT_DATA;
- warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data);
- }
-
- if (!no_error){
- warn("error in rx desc->status, epid %d, first urb = 0x%lx",
- epid, (unsigned long)urb);
- __dump_in_desc(myNextRxDesc);
-
- warn("R_USB_STATUS = 0x%x", *R_USB_STATUS);
-
- /* Check that ept was disabled when error occurred. */
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_BULK:
- assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
- break;
- case PIPE_CONTROL:
- assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
- break;
- case PIPE_INTERRUPT:
- assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable)));
- break;
- case PIPE_ISOCHRONOUS:
- assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)));
- break;
- default:
- warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p",
- usb_pipetype(urb->pipe),
- urb);
- }
- etrax_usb_complete_urb(urb, -EPROTO);
- goto skip_out;
- }
- }
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if ((usb_pipetype(urb->pipe) == PIPE_BULK) ||
- (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
- (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
-
- if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
- /* We get nodata for empty data transactions, and the rx descriptor's
- hw_len field is not valid in that case. No data to copy in other
- words. */
- } else {
- /* Make sure the data fits in the buffer. */
- assert(urb_priv->rx_offset + myNextRxDesc->hw_len
- <= urb->transfer_buffer_length);
-
- memcpy(urb->transfer_buffer + urb_priv->rx_offset,
- phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
- urb_priv->rx_offset += myNextRxDesc->hw_len;
- }
-
- if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
- if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) &&
- ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) ==
- IO_STATE(USB_EP_command, enable, yes))) {
- /* The EP is still enabled, so the OUT packet used to ack
- the in data is probably not processed yet. If the EP
- sub pointer has not moved beyond urb_priv->last_sb mark
- it for a descriptor interrupt and complete the urb in
- the descriptor interrupt handler.
- */
- USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0;
-
- while ((sub != NULL) && (sub != urb_priv->last_sb)) {
- sub = sub->next ? phys_to_virt(sub->next) : 0;
- }
- if (sub != NULL) {
- /* The urb has not been fully processed. */
- urb_priv->urb_state = WAITING_FOR_DESCR_INTR;
- } else {
- warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub);
- etrax_usb_complete_urb(urb, 0);
- }
- } else {
- etrax_usb_complete_urb(urb, 0);
- }
- }
-
- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
- struct usb_iso_packet_descriptor *packet;
-
- if (urb_priv->urb_state == UNLINK) {
- info("Ignoring rx data for urb being unlinked.");
- goto skip_out;
- } else if (urb_priv->urb_state == NOT_STARTED) {
- info("What? Got rx data for urb that isn't started?");
- goto skip_out;
- }
-
- packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
- packet->status = 0;
-
- if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
- /* We get nodata for empty data transactions, and the rx descriptor's
- hw_len field is not valid in that case. We copy 0 bytes however to
- stay in synch. */
- packet->actual_length = 0;
- } else {
- packet->actual_length = myNextRxDesc->hw_len;
- /* Make sure the data fits in the buffer. */
- assert(packet->actual_length <= packet->length);
- memcpy(urb->transfer_buffer + packet->offset,
- phys_to_virt(myNextRxDesc->buf), packet->actual_length);
- }
-
- /* Increment the packet counter. */
- urb_priv->isoc_packet_counter++;
-
- /* Note that we don't care about the eot field in the rx descriptor's status.
- It will always be set for isoc traffic. */
- if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
-
- /* Out-of-synch diagnostics. */
- curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
- if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
- /* This test is wrong, if there is more than one isoc
- in endpoint active it will always calculate wrong
- since prev_fm is shared by all endpoints.
-
- FIXME Make this check per URB using urb->start_frame.
- */
- dbg_isoc("Out of synch? Previous frame = %d, current frame = %d",
- prev_fm, curr_fm);
-
- }
- prev_fm = curr_fm;
-
- /* Complete the urb with status OK. */
- etrax_usb_complete_isoc_urb(urb, 0);
- }
- }
-
- skip_out:
-
- /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
- has the same layout as USB_IN_Desc for the relevant fields.) */
- prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
-
- myPrevRxDesc = myNextRxDesc;
- myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
- myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
- myLastRxDesc = myPrevRxDesc;
-
- myNextRxDesc->status = 0;
- myNextRxDesc = phys_to_virt(myNextRxDesc->next);
- }
-
- restore_flags(flags);
-
- DBFEXIT;
-
- return IRQ_HANDLED;
-}
-
-
-/* This function will unlink the SB descriptors associated with this urb. */
-static int etrax_remove_from_sb_list(struct urb *urb)
-{
- USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
- etrax_urb_priv_t *urb_priv;
- int i = 0;
-
- DBFENTER;
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
- doesn't really need to be disabled, it's just that we expect it to be. */
- if (usb_pipetype(urb->pipe) == PIPE_BULK) {
- assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
- assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
- }
-
- first_sb = urb_priv->first_sb;
- last_sb = urb_priv->last_sb;
-
- assert(first_sb);
- assert(last_sb);
-
- while (first_sb != last_sb) {
- next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
- kmem_cache_free(usb_desc_cache, first_sb);
- first_sb = next_sb;
- i++;
- }
- kmem_cache_free(usb_desc_cache, last_sb);
- i++;
- dbg_sb("%d SB descriptors freed", i);
- /* Compare i with urb->number_of_packets for Isoc traffic.
- Should be same when calling unlink_urb */
-
- DBFEXIT;
-
- return i;
-}
-
-static int etrax_usb_submit_bulk_urb(struct urb *urb)
-{
- int epid;
- int empty;
- unsigned long flags;
- etrax_urb_priv_t *urb_priv;
-
- DBFENTER;
-
- /* Epid allocation, empty check and list add must be protected.
- Read about this in etrax_usb_submit_ctrl_urb. */
-
- spin_lock_irqsave(&urb_list_lock, flags);
- epid = etrax_usb_setup_epid(urb);
- if (epid == -1) {
- DBFEXIT;
- spin_unlock_irqrestore(&urb_list_lock, flags);
- return -ENOMEM;
- }
- empty = urb_list_empty(epid);
- urb_list_add(urb, epid);
- spin_unlock_irqrestore(&urb_list_lock, flags);
-
- dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d",
- usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
-
- /* Mark the urb as being in progress. */
- urb->status = -EINPROGRESS;
-
- /* Setup the hcpriv data. */
- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
- assert(urb_priv != NULL);
- /* This sets rx_offset to 0. */
- urb_priv->urb_state = NOT_STARTED;
- urb->hcpriv = urb_priv;
-
- if (empty) {
- etrax_usb_add_to_bulk_sb_list(urb, epid);
- }
-
- DBFEXIT;
-
- return 0;
-}
-
-static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid)
-{
- USB_SB_Desc_t *sb_desc;
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- unsigned long flags;
- char maxlen;
-
- DBFENTER;
-
- dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
-
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-
- sb_desc = kmem_cache_zalloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc != NULL);
-
-
- if (usb_pipeout(urb->pipe)) {
-
- dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
-
- /* This is probably a sanity check of the bulk transaction length
- not being larger than 64 kB. */
- if (urb->transfer_buffer_length > 0xffff) {
- panic("urb->transfer_buffer_length > 0xffff");
- }
-
- sb_desc->sw_len = urb->transfer_buffer_length;
-
- /* The rem field is don't care if it's not a full-length transfer, so setting
- it shouldn't hurt. Also, rem isn't used for OUT traffic. */
- sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, out) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- /* The full field is set to yes, even if we don't actually check that this is
- a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
- Setting full prevents the USB controller from sending an empty packet in
- that case. However, if URB_ZERO_PACKET was set we want that. */
- if (!(urb->transfer_flags & URB_ZERO_PACKET)) {
- sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
- }
-
- sb_desc->buf = virt_to_phys(urb->transfer_buffer);
- sb_desc->next = 0;
-
- } else if (usb_pipein(urb->pipe)) {
-
- dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
-
- sb_desc->sw_len = urb->transfer_buffer_length ?
- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-
- /* The rem field is don't care if it's not a full-length transfer, so setting
- it shouldn't hurt. */
- sb_desc->command =
- (IO_FIELD(USB_SB_command, rem,
- urb->transfer_buffer_length % maxlen) |
- IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- sb_desc->buf = 0;
- sb_desc->next = 0;
- }
-
- urb_priv->first_sb = sb_desc;
- urb_priv->last_sb = sb_desc;
- urb_priv->epid = epid;
-
- urb->hcpriv = urb_priv;
-
- /* Reset toggle bits and reset error count. */
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
-
- /* FIXME: Is this a special case since the hold field is checked,
- or should we check hold in a lot of other cases as well? */
- if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
- panic("Hold was set in %s", __FUNCTION__);
- }
-
- /* Reset error counters (regardless of which direction this traffic is). */
- *R_USB_EPT_DATA &=
- ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
- IO_MASK(R_USB_EPT_DATA, error_count_out));
-
- /* Software must preset the toggle bits. */
- if (usb_pipeout(urb->pipe)) {
- char toggle =
- usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
- *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
- } else {
- char toggle =
- usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
- *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
- *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
- }
-
- /* Assert that the EP descriptor is disabled. */
- assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-
- /* The reason we set the EP's sub pointer directly instead of
- walking the SB list and linking it last in the list is that we only
- have one active urb at a time (the rest are queued). */
-
- /* Note that we cannot have interrupts running when we have set the SB descriptor
- but the EP is not yet enabled. If a bulk eot happens for another EP, we will
- find this EP disabled and with a SB != 0, which will make us think that it's done. */
- TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
- TxBulkEPList[epid].hw_len = 0;
- /* Note that we don't have to fill in the ep_id field since this
- was done when we allocated the EP descriptors in init_tx_bulk_ep. */
-
- /* Check if the dummy list is already with us (if several urbs were queued). */
- if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
-
- dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d",
- (unsigned long)urb, epid);
-
- /* The last EP in the dummy list already has its next pointer set to
- TxBulkEPList[epid].next. */
-
- /* We don't need to check if the DMA is at this EP or not before changing the
- next pointer, since we will do it in one 32-bit write (EP descriptors are
- 32-bit aligned). */
- TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
- }
- /* Enable the EP descr. */
- dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
- TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
- /* Everything is set up, safe to enable interrupts again. */
- restore_flags(flags);
-
- /* If the DMA bulk channel isn't running, we need to restart it if it
- has stopped at the last EP descriptor (DMA stopped because there was
- no more traffic) or if it has stopped at a dummy EP with the intr flag
- set (DMA stopped because we were too slow in inserting new traffic). */
- if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
-
- USB_EP_Desc_t *ep;
- ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
- dbg_bulk("DMA channel not running in add");
- dbg_bulk("DMA is at 0x%lx", (unsigned long)ep);
-
- if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) ||
- (ep->command & 0x8) >> 3) {
- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
- /* Update/restart the bulk start timer since we just started the channel. */
- mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
- /* Update/restart the bulk eot timer since we just inserted traffic. */
- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
- }
- }
-
- DBFEXIT;
-}
-
-static void etrax_usb_complete_bulk_urb(struct urb *urb, int status)
-{
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- int epid = urb_priv->epid;
- unsigned long flags;
-
- DBFENTER;
-
- if (status)
- warn("Completing bulk urb with status %d.", status);
-
- dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
-
- /* Update the urb list. */
- urb_list_del(urb, epid);
-
- /* For an IN pipe, we always set the actual length, regardless of whether there was
- an error or not (which means the device driver can use the data if it wants to). */
- if (usb_pipein(urb->pipe)) {
- urb->actual_length = urb_priv->rx_offset;
- } else {
- /* Set actual_length for OUT urbs also; the USB mass storage driver seems
- to want that. We wouldn't know of any partial writes if there was an error. */
- if (status == 0) {
- urb->actual_length = urb->transfer_buffer_length;
- } else {
- urb->actual_length = 0;
- }
- }
-
- /* FIXME: Is there something of the things below we shouldn't do if there was an error?
- Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
-
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
-
- /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
- if (usb_pipeout(urb->pipe)) {
- char toggle =
- IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe), toggle);
- } else {
- char toggle =
- IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe), toggle);
- }
- restore_flags(flags);
-
- /* Remember to free the SBs. */
- etrax_remove_from_sb_list(urb);
- kfree(urb_priv);
- urb->hcpriv = 0;
-
- /* If there are any more urb's in the list we'd better start sending */
- if (!urb_list_empty(epid)) {
-
- struct urb *new_urb;
-
- /* Get the first urb. */
- new_urb = urb_list_first(epid);
- assert(new_urb);
-
- dbg_bulk("More bulk for epid %d", epid);
-
- etrax_usb_add_to_bulk_sb_list(new_urb, epid);
- }
-
- urb->status = status;
-
- /* We let any non-zero status from the layer above have precedence. */
- if (status == 0) {
- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
- is to be treated as an error. */
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- if (usb_pipein(urb->pipe) &&
- (urb->actual_length !=
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
- urb->status = -EREMOTEIO;
- }
- }
- }
-
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- if (urb_list_empty(epid)) {
- /* This means that this EP is now free, deconfigure it. */
- etrax_usb_free_epid(epid);
-
- /* No more traffic; time to clean up.
- Must set sub pointer to 0, since we look at the sub pointer when handling
- the bulk eot interrupt. */
-
- dbg_bulk("No bulk for epid %d", epid);
-
- TxBulkEPList[epid].sub = 0;
-
- /* Unlink the dummy list. */
-
- dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d",
- (unsigned long)urb, epid);
-
- /* No need to wait for the DMA before changing the next pointer.
- The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use
- the last one (INVALID_EPID) for actual traffic. */
- TxBulkEPList[epid].next =
- virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
- }
-
- DBFEXIT;
-}
-
-static int etrax_usb_submit_ctrl_urb(struct urb *urb)
-{
- int epid;
- int empty;
- unsigned long flags;
- etrax_urb_priv_t *urb_priv;
-
- DBFENTER;
-
- /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
-
- /* Epid allocation, empty check and list add must be protected.
-
- Epid allocation because if we find an existing epid for this endpoint an urb might be
- completed (emptying the list) before we add the new urb to the list, causing the epid
- to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
-
- Empty check and add because otherwise we might conclude that the list is not empty,
- after which it becomes empty before we add the new urb to the list, causing us not to
- insert the new traffic into the SB list. */
-
- spin_lock_irqsave(&urb_list_lock, flags);
- epid = etrax_usb_setup_epid(urb);
- if (epid == -1) {
- spin_unlock_irqrestore(&urb_list_lock, flags);
- DBFEXIT;
- return -ENOMEM;
- }
- empty = urb_list_empty(epid);
- urb_list_add(urb, epid);
- spin_unlock_irqrestore(&urb_list_lock, flags);
-
- dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d",
- (unsigned long)urb, empty ? "empty" : "", epid);
-
- /* Mark the urb as being in progress. */
- urb->status = -EINPROGRESS;
-
- /* Setup the hcpriv data. */
- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
- assert(urb_priv != NULL);
- /* This sets rx_offset to 0. */
- urb_priv->urb_state = NOT_STARTED;
- urb->hcpriv = urb_priv;
-
- if (empty) {
- etrax_usb_add_to_ctrl_sb_list(urb, epid);
- }
-
- DBFEXIT;
-
- return 0;
-}
-
-static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid)
-{
- USB_SB_Desc_t *sb_desc_setup;
- USB_SB_Desc_t *sb_desc_data;
- USB_SB_Desc_t *sb_desc_status;
-
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-
- unsigned long flags;
- char maxlen;
-
- DBFENTER;
-
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-
- sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc_setup != NULL);
- sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc_status != NULL);
-
- /* Initialize the mandatory setup SB descriptor (used only in control transfers) */
- sb_desc_setup->sw_len = 8;
- sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, setup) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes));
-
- sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
-
- if (usb_pipeout(urb->pipe)) {
- dbg_ctrl("Transfer for epid %d is OUT", epid);
-
- /* If this Control OUT transfer has an optional data stage we add an OUT token
- before the mandatory IN (status) token, hence the reordered SB list */
-
- sb_desc_setup->next = virt_to_phys(sb_desc_status);
- if (urb->transfer_buffer) {
-
- dbg_ctrl("This OUT transfer has an extra data stage");
-
- sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc_data != NULL);
-
- sb_desc_setup->next = virt_to_phys(sb_desc_data);
-
- sb_desc_data->sw_len = urb->transfer_buffer_length;
- sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes));
- sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
- sb_desc_data->next = virt_to_phys(sb_desc_status);
- }
-
- sb_desc_status->sw_len = 1;
- sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, intr, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- sb_desc_status->buf = 0;
- sb_desc_status->next = 0;
-
- } else if (usb_pipein(urb->pipe)) {
-
- dbg_ctrl("Transfer for epid %d is IN", epid);
- dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
- dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
-
- sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(sb_desc_data != NULL);
-
- sb_desc_setup->next = virt_to_phys(sb_desc_data);
-
- sb_desc_data->sw_len = urb->transfer_buffer_length ?
- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
- dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
-
- sb_desc_data->command =
- (IO_FIELD(USB_SB_command, rem,
- urb->transfer_buffer_length % maxlen) |
- IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes));
-
- sb_desc_data->buf = 0;
- sb_desc_data->next = virt_to_phys(sb_desc_status);
-
- /* Read comment at zout_buffer declaration for an explanation to this. */
- sb_desc_status->sw_len = 1;
- sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, zout) |
- IO_STATE(USB_SB_command, full, yes) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, intr, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
- sb_desc_status->next = 0;
- }
-
- urb_priv->first_sb = sb_desc_setup;
- urb_priv->last_sb = sb_desc_status;
- urb_priv->epid = epid;
-
- urb_priv->urb_state = STARTED;
-
- /* Reset toggle bits and reset error count, remember to di and ei */
- /* Warning: it is possible that this locking doesn't work with bottom-halves */
-
- save_flags(flags);
- cli();
-
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
- panic("Hold was set in %s", __FUNCTION__);
- }
-
-
- /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
- are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
- in Designer's Reference, p. 8 - 11. */
- *R_USB_EPT_DATA &=
- ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
- IO_MASK(R_USB_EPT_DATA, error_count_out) |
- IO_MASK(R_USB_EPT_DATA, t_in) |
- IO_MASK(R_USB_EPT_DATA, t_out));
-
- /* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now
- (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
- restore_flags(flags);
-
- /* Assert that the EP descriptor is disabled. */
- assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-
- /* Set up and enable the EP descriptor. */
- TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
- TxCtrlEPList[epid].hw_len = 0;
- TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
- /* We start the DMA sub channel without checking if it's running or not, because:
- 1) If it's already running, issuing the start command is a nop.
- 2) We avoid a test-and-set race condition. */
- *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status)
-{
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- int epid = urb_priv->epid;
-
- DBFENTER;
-
- if (status)
- warn("Completing ctrl urb with status %d.", status);
-
- dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
-
- /* Remove this urb from the list. */
- urb_list_del(urb, epid);
-
- /* For an IN pipe, we always set the actual length, regardless of whether there was
- an error or not (which means the device driver can use the data if it wants to). */
- if (usb_pipein(urb->pipe)) {
- urb->actual_length = urb_priv->rx_offset;
- }
-
- /* FIXME: Is there something of the things below we shouldn't do if there was an error?
- Like, maybe we shouldn't insert more traffic. */
-
- /* Remember to free the SBs. */
- etrax_remove_from_sb_list(urb);
- kfree(urb_priv);
- urb->hcpriv = 0;
-
- /* If there are any more urbs in the list we'd better start sending. */
- if (!urb_list_empty(epid)) {
- struct urb *new_urb;
-
- /* Get the first urb. */
- new_urb = urb_list_first(epid);
- assert(new_urb);
-
- dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
-
- etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
- }
-
- urb->status = status;
-
- /* We let any non-zero status from the layer above have precedence. */
- if (status == 0) {
- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
- is to be treated as an error. */
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- if (usb_pipein(urb->pipe) &&
- (urb->actual_length !=
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
- urb->status = -EREMOTEIO;
- }
- }
- }
-
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- if (urb_list_empty(epid)) {
- /* No more traffic. Time to clean up. */
- etrax_usb_free_epid(epid);
- /* Must set sub pointer to 0. */
- dbg_ctrl("No ctrl for epid %d", epid);
- TxCtrlEPList[epid].sub = 0;
- }
-
- DBFEXIT;
-}
-
-static int etrax_usb_submit_intr_urb(struct urb *urb)
-{
-
- int epid;
-
- DBFENTER;
-
- if (usb_pipeout(urb->pipe)) {
- /* Unsupported transfer type.
- We don't support interrupt out traffic. (If we do, we can't support
- intervals for neither in or out traffic, but are forced to schedule all
- interrupt traffic in one frame.) */
- return -EINVAL;
- }
-
- epid = etrax_usb_setup_epid(urb);
- if (epid == -1) {
- DBFEXIT;
- return -ENOMEM;
- }
-
- if (!urb_list_empty(epid)) {
- /* There is already a queued urb for this endpoint. */
- etrax_usb_free_epid(epid);
- return -ENXIO;
- }
-
- urb->status = -EINPROGRESS;
-
- dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
-
- urb_list_add(urb, epid);
- etrax_usb_add_to_intr_sb_list(urb, epid);
-
- return 0;
-
- DBFEXIT;
-}
-
-static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid)
-{
-
- volatile USB_EP_Desc_t *tmp_ep;
- volatile USB_EP_Desc_t *first_ep;
-
- char maxlen;
- int interval;
- int i;
-
- etrax_urb_priv_t *urb_priv;
-
- DBFENTER;
-
- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- interval = urb->interval;
-
- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
- assert(urb_priv != NULL);
- urb->hcpriv = urb_priv;
-
- first_ep = &TxIntrEPList[0];
-
- /* Round of the interval to 2^n, it is obvious that this code favours
- smaller numbers, but that is actually a good thing */
- /* FIXME: The "rounding error" for larger intervals will be quite
- large. For in traffic this shouldn't be a problem since it will only
- mean that we "poll" more often. */
- for (i = 0; interval; i++) {
- interval = interval >> 1;
- }
- interval = 1 << (i - 1);
-
- dbg_intr("Interval rounded to %d", interval);
-
- tmp_ep = first_ep;
- i = 0;
- do {
- if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
- if ((i % interval) == 0) {
- /* Insert the traffic ep after tmp_ep */
- USB_EP_Desc_t *ep_desc;
- USB_SB_Desc_t *sb_desc;
-
- dbg_intr("Inserting EP for epid %d", epid);
-
- ep_desc = (USB_EP_Desc_t *)
- kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- sb_desc = (USB_SB_Desc_t *)
- kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
- assert(ep_desc != NULL);
- CHECK_ALIGN(ep_desc);
- assert(sb_desc != NULL);
-
- ep_desc->sub = virt_to_phys(sb_desc);
- ep_desc->hw_len = 0;
- ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
- IO_STATE(USB_EP_command, enable, yes));
-
-
- /* Round upwards the number of packets of size maxlen
- that this SB descriptor should receive. */
- sb_desc->sw_len = urb->transfer_buffer_length ?
- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
- sb_desc->next = 0;
- sb_desc->buf = 0;
- sb_desc->command =
- (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
- IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- ep_desc->next = tmp_ep->next;
- tmp_ep->next = virt_to_phys(ep_desc);
- }
- i++;
- }
- tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
- } while (tmp_ep != first_ep);
-
-
- /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
- urb_priv->epid = epid;
-
- /* We start the DMA sub channel without checking if it's running or not, because:
- 1) If it's already running, issuing the start command is a nop.
- 2) We avoid a test-and-set race condition. */
- *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-
-
-static void etrax_usb_complete_intr_urb(struct urb *urb, int status)
-{
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- int epid = urb_priv->epid;
-
- DBFENTER;
-
- if (status)
- warn("Completing intr urb with status %d.", status);
-
- dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
-
- urb->status = status;
- urb->actual_length = urb_priv->rx_offset;
-
- dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
-
- /* We let any non-zero status from the layer above have precedence. */
- if (status == 0) {
- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
- is to be treated as an error. */
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- if (urb->actual_length !=
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
- urb->status = -EREMOTEIO;
- }
- }
- }
-
- /* The driver will resubmit the URB so we need to remove it first */
- etrax_usb_unlink_urb(urb, 0);
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- DBFEXIT;
-}
-
-
-static int etrax_usb_submit_isoc_urb(struct urb *urb)
-{
- int epid;
- unsigned long flags;
-
- DBFENTER;
-
- dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
-
- /* Epid allocation, empty check and list add must be protected.
- Read about this in etrax_usb_submit_ctrl_urb. */
-
- spin_lock_irqsave(&urb_list_lock, flags);
- /* Is there an active epid for this urb ? */
- epid = etrax_usb_setup_epid(urb);
- if (epid == -1) {
- DBFEXIT;
- spin_unlock_irqrestore(&urb_list_lock, flags);
- return -ENOMEM;
- }
-
- /* Ok, now we got valid endpoint, lets insert some traffic */
-
- urb->status = -EINPROGRESS;
-
- /* Find the last urb in the URB_List and add this urb after that one.
- Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list. This
- is important to make this in "real time" since isochronous traffic is
- time sensitive. */
-
- dbg_isoc("Adding isoc urb to (possibly empty) list");
- urb_list_add(urb, epid);
- etrax_usb_add_to_isoc_sb_list(urb, epid);
- spin_unlock_irqrestore(&urb_list_lock, flags);
-
- DBFEXIT;
-
- return 0;
-}
-
-static void etrax_usb_check_error_isoc_ep(const int epid)
-{
- unsigned long int flags;
- int error_code;
- __u32 r_usb_ept_data;
-
- /* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof,
- bulk_eot and epid_attn interrupts. So we just check the status of
- the epid without testing if for it in R_USB_EPID_ATTN. */
-
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
- registers, they are located at the same address and are of the same size.
- In other words, this read should be ok for isoc also. */
- r_usb_ept_data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
-
- if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
- warn("Hold was set for epid %d.", epid);
- return;
- }
-
- if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) {
-
- /* This indicates that the SB list of the ept was completed before
- new data was appended to it. This is not an error, but indicates
- large system or USB load and could possibly cause trouble for
- very timing sensitive USB device drivers so we log it.
- */
- info("Isoc. epid %d disabled with no error", epid);
- return;
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) {
- /* Not really a protocol error, just says that the endpoint gave
- a stall response. Note that error_code cannot be stall for isoc. */
- panic("Isoc traffic cannot stall");
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) {
- /* Two devices responded to a transaction request. Must be resolved
- by software. FIXME: Reset ports? */
- panic("Bus error for epid %d."
- " Two devices responded to transaction request",
- epid);
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
- /* DMA overrun or underrun. */
- warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
-
- /* It seems that error_code = buffer_error in
- R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
- are the same error. */
- }
-}
-
-
-static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid)
-{
-
- int i = 0;
-
- etrax_urb_priv_t *urb_priv;
- USB_SB_Desc_t *prev_sb_desc, *next_sb_desc, *temp_sb_desc;
-
- DBFENTER;
-
- prev_sb_desc = next_sb_desc = temp_sb_desc = NULL;
-
- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
- assert(urb_priv != NULL);
-
- urb->hcpriv = urb_priv;
- urb_priv->epid = epid;
-
- if (usb_pipeout(urb->pipe)) {
-
- if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n");
-
- dbg_isoc("Transfer for epid %d is OUT", epid);
- dbg_isoc("%d packets in URB", urb->number_of_packets);
-
- /* Create one SB descriptor for each packet and link them together. */
- for (i = 0; i < urb->number_of_packets; i++) {
- if (!urb->iso_frame_desc[i].length)
- continue;
-
- next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC);
- assert(next_sb_desc != NULL);
-
- if (urb->iso_frame_desc[i].length > 0) {
-
- next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
- IO_STATE(USB_SB_command, eot, yes));
-
- next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
- next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset);
-
- /* Check if full length transfer. */
- if (urb->iso_frame_desc[i].length ==
- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
- next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
- }
- } else {
- dbg_isoc("zero len packet");
- next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
- IO_STATE(USB_SB_command, tt, zout) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, full, yes));
-
- next_sb_desc->sw_len = 1;
- next_sb_desc->buf = virt_to_phys(&zout_buffer[0]);
- }
-
- /* First SB descriptor that belongs to this urb */
- if (i == 0)
- urb_priv->first_sb = next_sb_desc;
- else
- prev_sb_desc->next = virt_to_phys(next_sb_desc);
-
- prev_sb_desc = next_sb_desc;
- }
-
- next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) |
- IO_STATE(USB_SB_command, eol, yes));
- next_sb_desc->next = 0;
- urb_priv->last_sb = next_sb_desc;
-
- } else if (usb_pipein(urb->pipe)) {
-
- dbg_isoc("Transfer for epid %d is IN", epid);
- dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
- dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
-
- /* Note that in descriptors for periodic traffic are not consumed. This means that
- the USB controller never propagates in the SB list. In other words, if there already
- is an SB descriptor in the list for this EP we don't have to do anything. */
- if (TxIsocEPList[epid].sub == 0) {
- dbg_isoc("Isoc traffic not already running, allocating SB");
-
- next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC);
- assert(next_sb_desc != NULL);
-
- next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
- IO_STATE(USB_SB_command, eot, yes) |
- IO_STATE(USB_SB_command, eol, yes));
-
- next_sb_desc->next = 0;
- next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant
- for periodic in traffic as long as it is more
- than zero. Set to 1 always. */
- next_sb_desc->buf = 0;
-
- /* The rem field is don't care for isoc traffic, so we don't set it. */
-
- /* Only one SB descriptor that belongs to this urb. */
- urb_priv->first_sb = next_sb_desc;
- urb_priv->last_sb = next_sb_desc;
-
- } else {
-
- dbg_isoc("Isoc traffic already running, just setting first/last_sb");
-
- /* Each EP for isoc in will have only one SB descriptor, setup when submitting the
- already active urb. Note that even though we may have several first_sb/last_sb
- pointing at the same SB descriptor, they are freed only once (when the list has
- become empty). */
- urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
- urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
- return;
- }
-
- }
-
- /* Find the spot to insert this urb and add it. */
- if (TxIsocEPList[epid].sub == 0) {
- /* First SB descriptor inserted in this list (in or out). */
- dbg_isoc("Inserting SB desc first in list");
- TxIsocEPList[epid].hw_len = 0;
- TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
-
- } else {
- /* Isochronous traffic is already running, insert new traffic last (only out). */
- dbg_isoc("Inserting SB desc last in list");
- temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
- while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) !=
- IO_STATE(USB_SB_command, eol, yes)) {
- assert(temp_sb_desc->next);
- temp_sb_desc = phys_to_virt(temp_sb_desc->next);
- }
- dbg_isoc("Appending list on desc 0x%p", temp_sb_desc);
-
- /* Next pointer must be set before eol is removed. */
- temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);
- /* Clear the previous end of list flag since there is a new in the
- added SB descriptor list. */
- temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
-
- if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
- /* 8.8.5 in Designer's Reference says we should check for and correct
- any errors in the EP here. That should not be necessary if epid_attn
- is handled correctly, so we assume all is ok. */
- dbg_isoc("EP disabled");
- etrax_usb_check_error_isoc_ep(epid);
-
- /* The SB list was exhausted. */
- if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) {
- /* The new sublist did not get processed before the EP was
- disabled. Setup the EP again. */
- dbg_isoc("Set EP sub to new list");
- TxIsocEPList[epid].hw_len = 0;
- TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
- }
- }
- }
-
- if (urb->transfer_flags & URB_ISO_ASAP) {
- /* The isoc transfer should be started as soon as possible. The start_frame
- field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
- with a USB Chief trace shows that the first isoc IN token is sent 2 frames
- later. I'm not sure how this affects usage of the start_frame field by the
- device driver, or how it affects things when USB_ISO_ASAP is not set, so
- therefore there's no compensation for the 2 frame "lag" here. */
- urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
- urb_priv->urb_state = STARTED;
- dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
- } else {
- /* Not started yet. */
- urb_priv->urb_state = NOT_STARTED;
- dbg_isoc("urb_priv->urb_state set to NOT_STARTED");
- }
-
- /* We start the DMA sub channel without checking if it's running or not, because:
- 1) If it's already running, issuing the start command is a nop.
- 2) We avoid a test-and-set race condition. */
- *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
-
- DBFEXIT;
-}
-
-static void etrax_usb_complete_isoc_urb(struct urb *urb, int status)
-{
- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- int epid = urb_priv->epid;
- int auto_resubmit = 0;
-
- DBFENTER;
- dbg_isoc("complete urb 0x%p, status %d", urb, status);
-
- if (status)
- warn("Completing isoc urb with status %d.", status);
-
- if (usb_pipein(urb->pipe)) {
- int i;
-
- /* Make that all isoc packets have status and length set before
- completing the urb. */
- for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) {
- urb->iso_frame_desc[i].actual_length = 0;
- urb->iso_frame_desc[i].status = -EPROTO;
- }
-
- urb_list_del(urb, epid);
-
- if (!list_empty(&urb_list[epid])) {
- ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
- } else {
- unsigned long int flags;
- if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- /* The EP was enabled, disable it and wait. */
- TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-
- /* Ah, the luxury of busy-wait. */
- while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
- }
-
- etrax_remove_from_sb_list(urb);
- TxIsocEPList[epid].sub = 0;
- TxIsocEPList[epid].hw_len = 0;
-
- save_flags(flags);
- cli();
- etrax_usb_free_epid(epid);
- restore_flags(flags);
- }
-
- urb->hcpriv = 0;
- kfree(urb_priv);
-
- /* Release allocated bandwidth. */
- usb_release_bandwidth(urb->dev, urb, 0);
- } else if (usb_pipeout(urb->pipe)) {
- int freed_descr;
-
- dbg_isoc("Isoc out urb complete 0x%p", urb);
-
- /* Update the urb list. */
- urb_list_del(urb, epid);
-
- freed_descr = etrax_remove_from_sb_list(urb);
- dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets);
- assert(freed_descr == urb->number_of_packets);
- urb->hcpriv = 0;
- kfree(urb_priv);
-
- /* Release allocated bandwidth. */
- usb_release_bandwidth(urb->dev, urb, 0);
- }
-
- urb->status = status;
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
-
- if (auto_resubmit) {
- /* Check that urb was not unlinked by the complete callback. */
- if (__urb_list_entry(urb, epid)) {
- /* Move this one down the list. */
- urb_list_move_last(urb, epid);
-
- /* Mark the now first urb as started (may already be). */
- ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
-
- /* Must set this to 0 since this urb is still active after
- completion. */
- urb_priv->isoc_packet_counter = 0;
- } else {
- warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb);
- }
- }
-
- DBFEXIT;
-}
-
-static void etrax_usb_complete_urb(struct urb *urb, int status)
-{
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_BULK:
- etrax_usb_complete_bulk_urb(urb, status);
- break;
- case PIPE_CONTROL:
- etrax_usb_complete_ctrl_urb(urb, status);
- break;
- case PIPE_INTERRUPT:
- etrax_usb_complete_intr_urb(urb, status);
- break;
- case PIPE_ISOCHRONOUS:
- etrax_usb_complete_isoc_urb(urb, status);
- break;
- default:
- err("Unknown pipetype");
- }
-}
-
-
-
-static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc)
-{
- usb_interrupt_registers_t *reg;
- unsigned long flags;
- __u32 irq_mask;
- __u8 status;
- __u32 epid_attn;
- __u16 port_status_1;
- __u16 port_status_2;
- __u32 fm_number;
-
- DBFENTER;
-
- /* Read critical registers into local variables, do kmalloc afterwards. */
- save_flags(flags);
- cli();
-
- irq_mask = *R_USB_IRQ_MASK_READ;
- /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
- must be read before R_USB_EPID_ATTN since reading the latter clears the
- ourun and perror fields of R_USB_STATUS. */
- status = *R_USB_STATUS;
-
- /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
- epid_attn = *R_USB_EPID_ATTN;
-
- /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
- port_status interrupt. */
- port_status_1 = *R_USB_RH_PORT_STATUS_1;
- port_status_2 = *R_USB_RH_PORT_STATUS_2;
-
- /* Reading R_USB_FM_NUMBER clears the sof interrupt. */
- /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
- fm_number = *R_USB_FM_NUMBER;
-
- restore_flags(flags);
-
- reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, GFP_ATOMIC);
-
- assert(reg != NULL);
-
- reg->hc = (etrax_hc_t *)vhc;
-
- /* Now put register values into kmalloc'd area. */
- reg->r_usb_irq_mask_read = irq_mask;
- reg->r_usb_status = status;
- reg->r_usb_epid_attn = epid_attn;
- reg->r_usb_rh_port_status_1 = port_status_1;
- reg->r_usb_rh_port_status_2 = port_status_2;
- reg->r_usb_fm_number = fm_number;
-
- INIT_WORK(&reg->usb_bh, etrax_usb_hc_interrupt_bottom_half, reg);
- schedule_work(&reg->usb_bh);
-
- DBFEXIT;
-
- return IRQ_HANDLED;
-}
-
-static void etrax_usb_hc_interrupt_bottom_half(void *data)
-{
- usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
- __u32 irq_mask = reg->r_usb_irq_mask_read;
-
- DBFENTER;
-
- /* Interrupts are handled in order of priority. */
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
- etrax_usb_hc_epid_attn_interrupt(reg);
- }
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
- etrax_usb_hc_port_status_interrupt(reg);
- }
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
- etrax_usb_hc_ctl_status_interrupt(reg);
- }
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
- etrax_usb_hc_isoc_eof_interrupt();
- }
- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
- /* Update/restart the bulk start timer since obviously the channel is running. */
- mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
- /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-
- etrax_usb_hc_bulk_eot_interrupt(0);
- }
-
- kmem_cache_free(top_half_reg_cache, reg);
-
- DBFEXIT;
-}
-
-
-void etrax_usb_hc_isoc_eof_interrupt(void)
-{
- struct urb *urb;
- etrax_urb_priv_t *urb_priv;
- int epid;
- unsigned long flags;
-
- DBFENTER;
-
- /* Do not check the invalid epid (it has a valid sub pointer). */
- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
-
- /* Do not check the invalid epid (it has a valid sub pointer). */
- if ((epid == DUMMY_EPID) || (epid == INVALID_EPID))
- continue;
-
- /* Disable interrupts to block the isoc out descriptor interrupt handler
- from being called while the isoc EPID list is being checked.
- */
- save_flags(flags);
- cli();
-
- if (TxIsocEPList[epid].sub == 0) {
- /* Nothing here to see. */
- restore_flags(flags);
- continue;
- }
-
- /* Get the first urb (if any). */
- urb = urb_list_first(epid);
- if (urb == 0) {
- warn("Ignoring NULL urb");
- restore_flags(flags);
- continue;
- }
- if (usb_pipein(urb->pipe)) {
-
- /* Sanity check. */
- assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
-
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- if (urb_priv->urb_state == NOT_STARTED) {
-
- /* If ASAP is not set and urb->start_frame is the current frame,
- start the transfer. */
- if (!(urb->transfer_flags & URB_ISO_ASAP) &&
- (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
-
- dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
- /* This urb is now active. */
- urb_priv->urb_state = STARTED;
- continue;
- }
- }
- }
- restore_flags(flags);
- }
-
- DBFEXIT;
-
-}
-
-void etrax_usb_hc_bulk_eot_interrupt(int timer_induced)
-{
- int epid;
-
- /* The technique is to run one urb at a time, wait for the eot interrupt at which
- point the EP descriptor has been disabled. */
-
- DBFENTER;
- dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
-
- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-
- if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
- (TxBulkEPList[epid].sub != 0)) {
-
- struct urb *urb;
- etrax_urb_priv_t *urb_priv;
- unsigned long flags;
- __u32 r_usb_ept_data;
-
- /* Found a disabled EP descriptor which has a non-null sub pointer.
- Verify that this ctrl EP descriptor got disabled no errors.
- FIXME: Necessary to check error_code? */
- dbg_bulk("for epid %d?", epid);
-
- /* Get the first urb. */
- urb = urb_list_first(epid);
-
- /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
- wrong unlinking? */
- if (!urb) {
- warn("NULL urb for epid %d", epid);
- continue;
- }
-
- assert(urb);
- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
- assert(urb_priv);
-
- /* Sanity checks. */
- assert(usb_pipetype(urb->pipe) == PIPE_BULK);
- if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
- err("bulk endpoint got disabled before reaching last sb");
- }
-
- /* For bulk IN traffic, there seems to be a race condition between
- between the bulk eot and eop interrupts, or rather an uncertainty regarding
- the order in which they happen. Normally we expect the eop interrupt from
- DMA channel 9 to happen before the eot interrupt.
-
- Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
-
- if (usb_pipein(urb->pipe)) {
- dbg_bulk("in urb, continuing");
- continue;
- }
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- r_usb_ept_data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
- IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
- /* This means that the endpoint has no error, is disabled
- and had inserted traffic, i.e. transfer successfully completed. */
- etrax_usb_complete_bulk_urb(urb, 0);
- } else {
- /* Shouldn't happen. We expect errors to be caught by epid attention. */
- err("Found disabled bulk EP desc, error_code != no_error");
- }
- }
- }
-
- /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
- However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
- not. Also, we might find two disabled EPs when handling an eot interrupt, and then find
- none the next time. */
-
- DBFEXIT;
-
-}
-
-void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
-{
- /* This function handles the epid attention interrupt. There are a variety of reasons
- for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
-
- invalid ep_id - Invalid epid in an EP (EP disabled).
- stall - Not strictly an error condition (EP disabled).
- 3rd error - Three successive transaction errors (EP disabled).
- buffer ourun - Buffer overrun or underrun (EP disabled).
- past eof1 - Intr or isoc transaction proceeds past EOF1.
- near eof - Intr or isoc transaction would not fit inside the frame.
- zout transfer - If zout transfer for a bulk endpoint (EP disabled).
- setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
-
- int epid;
-
-
- DBFENTER;
-
- assert(reg != NULL);
-
- /* Note that we loop through all epids. We still want to catch errors for
- the invalid one, even though we might handle them differently. */
- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-
- if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
-
- struct urb *urb;
- __u32 r_usb_ept_data;
- unsigned long flags;
- int error_code;
-
- save_flags(flags);
- cli();
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
- nop();
- /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
- registers, they are located at the same address and are of the same size.
- In other words, this read should be ok for isoc also. */
- r_usb_ept_data = *R_USB_EPT_DATA;
- restore_flags(flags);
-
- /* First some sanity checks. */
- if (epid == INVALID_EPID) {
- /* FIXME: What if it became disabled? Could seriously hurt interrupt
- traffic. (Use do_intr_recover.) */
- warn("Got epid_attn for INVALID_EPID (%d).", epid);
- err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
- err("R_USB_STATUS = 0x%x", reg->r_usb_status);
- continue;
- } else if (epid == DUMMY_EPID) {
- /* We definitely don't care about these ones. Besides, they are
- always disabled, so any possible disabling caused by the
- epid attention interrupt is irrelevant. */
- warn("Got epid_attn for DUMMY_EPID (%d).", epid);
- continue;
- }
-
- /* Get the first urb in the urb list for this epid. We blatantly assume
- that only the first urb could have caused the epid attention.
- (For bulk and ctrl, only one urb is active at any one time. For intr
- and isoc we remove them once they are completed.) */
- urb = urb_list_first(epid);
-
- if (urb == NULL) {
- err("Got epid_attn for epid %i with no urb.", epid);
- err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
- err("R_USB_STATUS = 0x%x", reg->r_usb_status);
- continue;
- }
-
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_BULK:
- warn("Got epid attn for bulk endpoint, epid %d", epid);
- break;
- case PIPE_CONTROL:
- warn("Got epid attn for control endpoint, epid %d", epid);
- break;
- case PIPE_INTERRUPT:
- warn("Got epid attn for interrupt endpoint, epid %d", epid);
- break;
- case PIPE_ISOCHRONOUS:
- warn("Got epid attn for isochronous endpoint, epid %d", epid);
- break;
- }
-
- if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
- if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
- warn("Hold was set for epid %d.", epid);
- continue;
- }
- }
-
- /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and
- R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
- } else {
- error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
- }
-
- /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
- if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-
- /* Isoc traffic doesn't have error_count_in/error_count_out. */
- if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
- (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
- IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
- /* 3rd error. */
- warn("3rd error for epid %i", epid);
- etrax_usb_complete_urb(urb, -EPROTO);
-
- } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-
- warn("Perror for epid %d", epid);
-
- if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
- /* invalid ep_id */
- panic("Perror because of invalid epid."
- " Deconfigured too early?");
- } else {
- /* past eof1, near eof, zout transfer, setup transfer */
-
- /* Dump the urb and the relevant EP descriptor list. */
-
- __dump_urb(urb);
- __dump_ept_data(epid);
- __dump_ep_list(usb_pipetype(urb->pipe));
-
- panic("Something wrong with DMA descriptor contents."
- " Too much traffic inserted?");
- }
- } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
- /* buffer ourun */
- panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
- }
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
- /* Not really a protocol error, just says that the endpoint gave
- a stall response. Note that error_code cannot be stall for isoc. */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- panic("Isoc traffic cannot stall");
- }
-
- warn("Stall for epid %d", epid);
- etrax_usb_complete_urb(urb, -EPIPE);
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
- /* Two devices responded to a transaction request. Must be resolved
- by software. FIXME: Reset ports? */
- panic("Bus error for epid %d."
- " Two devices responded to transaction request",
- epid);
-
- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
- /* DMA overrun or underrun. */
- warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
-
- /* It seems that error_code = buffer_error in
- R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
- are the same error. */
- etrax_usb_complete_urb(urb, -EPROTO);
- }
- }
- }
-
- DBFEXIT;
-
-}
-
-void etrax_usb_bulk_start_timer_func(unsigned long dummy)
-{
-
- /* We might enable an EP descriptor behind the current DMA position when it's about
- to decide that there are no more bulk traffic and it should stop the bulk channel.
- Therefore we periodically check if the bulk channel is stopped and there is an
- enabled bulk EP descriptor, in which case we start the bulk channel. */
- dbg_bulk("bulk_start_timer timed out.");
-
- if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
- int epid;
-
- dbg_bulk("Bulk DMA channel not running.");
-
- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
- if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
- dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n",
- epid);
- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
-
- /* Restart the bulk eot timer since we just started the bulk channel. */
- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-
- /* No need to search any further. */
- break;
- }
- }
- } else {
- dbg_bulk("Bulk DMA channel running.");
- }
-}
-
-void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
-{
- etrax_hc_t *hc = reg->hc;
- __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
- __u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
-
- DBFENTER;
-
- /* The Etrax RH does not include a wPortChange register, so this has to be handled in software
- (by saving the old port status value for comparison when the port status interrupt happens).
- See section 11.16.2.6.2 in the USB 1.1 spec for details. */
-
- dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
- dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
- dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
- dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
-
- /* C_PORT_CONNECTION is set on any transition. */
- hc->rh.wPortChange_1 |=
- ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
- (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
- (1 << RH_PORT_CONNECTION) : 0;
-
- hc->rh.wPortChange_2 |=
- ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
- (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
- (1 << RH_PORT_CONNECTION) : 0;
-
- /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
- the port is disabled, not when it's enabled. */
- hc->rh.wPortChange_1 |=
- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
- (1 << RH_PORT_ENABLE) : 0;
-
- hc->rh.wPortChange_2 |=
- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
- (1 << RH_PORT_ENABLE) : 0;
-
- /* C_PORT_SUSPEND is set to one when the device has transitioned out
- of the suspended state, i.e. when suspend goes from one to zero. */
- hc->rh.wPortChange_1 |=
- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
- (1 << RH_PORT_SUSPEND) : 0;
-
- hc->rh.wPortChange_2 |=
- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
- (1 << RH_PORT_SUSPEND) : 0;
-
-
- /* C_PORT_RESET is set when reset processing on this port is complete. */
- hc->rh.wPortChange_1 |=
- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
- (1 << RH_PORT_RESET) : 0;
-
- hc->rh.wPortChange_2 |=
- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
- (1 << RH_PORT_RESET) : 0;
-
- /* Save the new values for next port status change. */
- hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
- hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;
-
- dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
- dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
-
- DBFEXIT;
-
-}
-
-void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
-{
- DBFENTER;
-
- /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
- list for the corresponding epid? */
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
- panic("USB controller got ourun.");
- }
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-
- /* Before, etrax_usb_do_intr_recover was called on this epid if it was
- an interrupt pipe. I don't see how re-enabling all EP descriptors
- will help if there was a programming error. */
- panic("USB controller got perror.");
- }
-
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
- /* We should never operate in device mode. */
- panic("USB controller in device mode.");
- }
-
- /* These if-statements could probably be nested. */
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
- info("USB controller in host mode.");
- }
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
- info("USB controller started.");
- }
- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
- info("USB controller running.");
- }
-
- DBFEXIT;
-
-}
-
-
-static int etrax_rh_submit_urb(struct urb *urb)
-{
- struct usb_device *usb_dev = urb->dev;
- etrax_hc_t *hc = usb_dev->bus->hcpriv;
- unsigned int pipe = urb->pipe;
- struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
- void *data = urb->transfer_buffer;
- int leni = urb->transfer_buffer_length;
- int len = 0;
- int stat = 0;
-
- __u16 bmRType_bReq;
- __u16 wValue;
- __u16 wIndex;
- __u16 wLength;
-
- DBFENTER;
-
- /* FIXME: What is this interrupt urb that is sent to the root hub? */
- if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
- dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
- hc->rh.urb = urb;
- hc->rh.send = 1;
- /* FIXME: We could probably remove this line since it's done
- in etrax_rh_init_int_timer. (Don't remove it from
- etrax_rh_init_int_timer though.) */
- hc->rh.interval = urb->interval;
- etrax_rh_init_int_timer(urb);
- DBFEXIT;
-
- return 0;
- }
-
- bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
- wValue = le16_to_cpu(cmd->wValue);
- wIndex = le16_to_cpu(cmd->wIndex);
- wLength = le16_to_cpu(cmd->wLength);
-
- dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
- dbg_rh("wValue : 0x%04x (%d)", wValue, wValue);
- dbg_rh("wIndex : 0x%04x (%d)", wIndex, wIndex);
- dbg_rh("wLength : 0x%04x (%d)", wLength, wLength);
-
- switch (bmRType_bReq) {
-
- /* Request Destination:
- without flags: Device,
- RH_INTERFACE: interface,
- RH_ENDPOINT: endpoint,
- RH_CLASS means HUB here,
- RH_OTHER | RH_CLASS almost ever means HUB_PORT here
- */
-
- case RH_GET_STATUS:
- *(__u16 *) data = cpu_to_le16 (1);
- OK (2);
-
- case RH_GET_STATUS | RH_INTERFACE:
- *(__u16 *) data = cpu_to_le16 (0);
- OK (2);
-
- case RH_GET_STATUS | RH_ENDPOINT:
- *(__u16 *) data = cpu_to_le16 (0);
- OK (2);
-
- case RH_GET_STATUS | RH_CLASS:
- *(__u32 *) data = cpu_to_le32 (0);
- OK (4); /* hub power ** */
-
- case RH_GET_STATUS | RH_OTHER | RH_CLASS:
- if (wIndex == 1) {
- *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
- *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
- } else if (wIndex == 2) {
- *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
- *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
- } else {
- dbg_rh("RH_GET_STATUS whith invalid wIndex!");
- OK(0);
- }
-
- OK(4);
-
- case RH_CLEAR_FEATURE | RH_ENDPOINT:
- switch (wValue) {
- case (RH_ENDPOINT_STALL):
- OK (0);
- }
- break;
-
- case RH_CLEAR_FEATURE | RH_CLASS:
- switch (wValue) {
- case (RH_C_HUB_OVER_CURRENT):
- OK (0); /* hub power over current ** */
- }
- break;
-
- case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case (RH_PORT_ENABLE):
- if (wIndex == 1) {
-
- dbg_rh("trying to do disable port 1");
-
- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
-
- while (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
- dbg_rh("Port 1 is disabled");
-
- } else if (wIndex == 2) {
-
- dbg_rh("trying to do disable port 2");
-
- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
-
- while (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
- dbg_rh("Port 2 is disabled");
-
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
- "with invalid wIndex == %d!", wIndex);
- }
-
- OK (0);
- case (RH_PORT_SUSPEND):
- /* Opposite to suspend should be resume, so we'll do a resume. */
- /* FIXME: USB 1.1, 11.16.2.2 says:
- "Clearing the PORT_SUSPEND feature causes a host-initiated resume
- on the specified port. If the port is not in the Suspended state,
- the hub should treat this request as a functional no-operation."
- Shouldn't we check if the port is in a suspended state before
- resuming? */
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- if (wIndex == 1) {
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port1) |
- IO_STATE(R_USB_COMMAND, port_cmd, resume) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
- } else if (wIndex == 2) {
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port2) |
- IO_STATE(R_USB_COMMAND, port_cmd, resume) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
- "with invalid wIndex == %d!", wIndex);
- }
-
- OK (0);
- case (RH_PORT_POWER):
- OK (0); /* port power ** */
- case (RH_C_PORT_CONNECTION):
- if (wIndex == 1) {
- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
- } else if (wIndex == 2) {
- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
- "with invalid wIndex == %d!", wIndex);
- }
-
- OK (0);
- case (RH_C_PORT_ENABLE):
- if (wIndex == 1) {
- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
- } else if (wIndex == 2) {
- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
- "with invalid wIndex == %d!", wIndex);
- }
- OK (0);
- case (RH_C_PORT_SUSPEND):
-/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
- OK (0);
- case (RH_C_PORT_OVER_CURRENT):
- OK (0); /* port power over current ** */
- case (RH_C_PORT_RESET):
- if (wIndex == 1) {
- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
- } else if (wIndex == 2) {
- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
- } else {
- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
- "with invalid index == %d!", wIndex);
- }
-
- OK (0);
-
- }
- break;
-
- case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case (RH_PORT_SUSPEND):
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- if (wIndex == 1) {
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port1) |
- IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
- } else if (wIndex == 2) {
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port2) |
- IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
- } else {
- dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND "
- "with invalid wIndex == %d!", wIndex);
- }
-
- OK (0);
- case (RH_PORT_RESET):
- if (wIndex == 1) {
-
- port_1_reset:
- dbg_rh("Doing reset of port 1");
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port1) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-
- /* We must wait at least 10 ms for the device to recover.
- 15 ms should be enough. */
- udelay(15000);
-
- /* Wait for reset bit to go low (should be done by now). */
- while (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
-
- /* If the port status is
- 1) connected and enabled then there is a device and everything is fine
- 2) neither connected nor enabled then there is no device, also fine
- 3) connected and not enabled then we try again
- (Yes, there are other port status combinations besides these.) */
-
- if ((hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
- (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
- dbg_rh("Connected device on port 1, but port not enabled?"
- " Trying reset again.");
- goto port_2_reset;
- }
-
- /* Diagnostic printouts. */
- if ((hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
- (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
- dbg_rh("No connected device on port 1");
- } else if ((hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
- (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
- dbg_rh("Connected device on port 1, port 1 enabled");
- }
-
- } else if (wIndex == 2) {
-
- port_2_reset:
- dbg_rh("Doing reset of port 2");
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* Issue the reset command. */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, port2) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
-
- /* We must wait at least 10 ms for the device to recover.
- 15 ms should be enough. */
- udelay(15000);
-
- /* Wait for reset bit to go low (should be done by now). */
- while (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
-
- /* If the port status is
- 1) connected and enabled then there is a device and everything is fine
- 2) neither connected nor enabled then there is no device, also fine
- 3) connected and not enabled then we try again
- (Yes, there are other port status combinations besides these.) */
-
- if ((hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
- (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
- dbg_rh("Connected device on port 2, but port not enabled?"
- " Trying reset again.");
- goto port_2_reset;
- }
-
- /* Diagnostic printouts. */
- if ((hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
- (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
- dbg_rh("No connected device on port 2");
- } else if ((hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
- (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
- dbg_rh("Connected device on port 2, port 2 enabled");
- }
-
- } else {
- dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
- }
-
- /* Make sure the controller isn't busy. */
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* If all enabled ports were disabled the host controller goes down into
- started mode, so we need to bring it back into the running state.
- (This is safe even if it's already in the running state.) */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, nop) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-
- dbg_rh("...Done");
- OK(0);
-
- case (RH_PORT_POWER):
- OK (0); /* port power ** */
- case (RH_PORT_ENABLE):
- /* There is no port enable command in the host controller, so if the
- port is already enabled, we do nothing. If not, we reset the port
- (with an ugly goto). */
-
- if (wIndex == 1) {
- if (hc->rh.prev_wPortStatus_1 &
- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
- goto port_1_reset;
- }
- } else if (wIndex == 2) {
- if (hc->rh.prev_wPortStatus_2 &
- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
- goto port_2_reset;
- }
- } else {
- dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
- }
- OK (0);
- }
- break;
-
- case RH_SET_ADDRESS:
- hc->rh.devnum = wValue;
- dbg_rh("RH address set to: %d", hc->rh.devnum);
- OK (0);
-
- case RH_GET_DESCRIPTOR:
- switch ((wValue & 0xff00) >> 8) {
- case (0x01): /* device descriptor */
- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength));
- memcpy (data, root_hub_dev_des, len);
- OK (len);
- case (0x02): /* configuration descriptor */
- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength));
- memcpy (data, root_hub_config_des, len);
- OK (len);
- case (0x03): /* string descriptors */
- len = usb_root_hub_string (wValue & 0xff,
- 0xff, "ETRAX 100LX",
- data, wLength);
- if (len > 0) {
- OK(min(leni, len));
- } else {
- stat = -EPIPE;
- }
-
- }
- break;
-
- case RH_GET_DESCRIPTOR | RH_CLASS:
- root_hub_hub_des[2] = hc->rh.numports;
- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
- memcpy (data, root_hub_hub_des, len);
- OK (len);
-
- case RH_GET_CONFIGURATION:
- *(__u8 *) data = 0x01;
- OK (1);
-
- case RH_SET_CONFIGURATION:
- OK (0);
-
- default:
- stat = -EPIPE;
- }
-
- urb->actual_length = len;
- urb->status = stat;
- urb->dev = NULL;
- if (urb->complete) {
- urb->complete(urb, NULL);
- }
- DBFEXIT;
-
- return 0;
-}
-
-static void
-etrax_usb_bulk_eot_timer_func(unsigned long dummy)
-{
- /* Because of a race condition in the top half, we might miss a bulk eot.
- This timer "simulates" a bulk eot if we don't get one for a while, hopefully
- correcting the situation. */
- dbg_bulk("bulk_eot_timer timed out.");
- etrax_usb_hc_bulk_eot_interrupt(1);
-}
-
-static void*
-etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size,
- unsigned mem_flags, dma_addr_t *dma)
-{
- return kmalloc(size, mem_flags);
-}
-
-static void
-etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma)
-{
- kfree(addr);
-}
-
-
-static struct device fake_device;
-
-static int __init etrax_usb_hc_init(void)
-{
- static etrax_hc_t *hc;
- struct usb_bus *bus;
- struct usb_device *usb_rh;
- int i;
-
- DBFENTER;
-
- info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
-
- hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
- assert(hc != NULL);
-
- /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
- /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
- SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
- sizeof(USB_SB_Desc_t). */
-
- usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0,
- SLAB_HWCACHE_ALIGN, 0, 0);
- assert(usb_desc_cache != NULL);
-
- top_half_reg_cache = kmem_cache_create("top_half_reg_cache",
- sizeof(usb_interrupt_registers_t),
- 0, SLAB_HWCACHE_ALIGN, 0, 0);
- assert(top_half_reg_cache != NULL);
-
- isoc_compl_cache = kmem_cache_create("isoc_compl_cache",
- sizeof(usb_isoc_complete_data_t),
- 0, SLAB_HWCACHE_ALIGN, 0, 0);
- assert(isoc_compl_cache != NULL);
-
- etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
- hc->bus = bus;
- bus->bus_name="ETRAX 100LX";
- bus->hcpriv = hc;
-
- /* Initialize RH to the default address.
- And make sure that we have no status change indication */
- hc->rh.numports = 2; /* The RH has two ports */
- hc->rh.devnum = 1;
- hc->rh.wPortChange_1 = 0;
- hc->rh.wPortChange_2 = 0;
-
- /* Also initate the previous values to zero */
- hc->rh.prev_wPortStatus_1 = 0;
- hc->rh.prev_wPortStatus_2 = 0;
-
- /* Initialize the intr-traffic flags */
- /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
- hc->intr.sleeping = 0;
- hc->intr.wq = NULL;
-
- epid_usage_bitmask = 0;
- epid_out_traffic = 0;
-
- /* Mark the invalid epid as being used. */
- set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
- nop();
- /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
- *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
- IO_FIELD(R_USB_EPT_DATA, max_len, 1));
-
- /* Mark the dummy epid as being used. */
- set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
- nop();
- *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
- IO_FIELD(R_USB_EPT_DATA, max_len, 1));
-
- /* Initialize the urb list by initiating a head for each list. */
- for (i = 0; i < NBR_OF_EPIDS; i++) {
- INIT_LIST_HEAD(&urb_list[i]);
- }
- spin_lock_init(&urb_list_lock);
-
- INIT_LIST_HEAD(&urb_unlink_list);
-
-
- /* Initiate the bulk start timer. */
- init_timer(&bulk_start_timer);
- bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
- bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
- add_timer(&bulk_start_timer);
-
-
- /* Initiate the bulk eot timer. */
- init_timer(&bulk_eot_timer);
- bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
- bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
- add_timer(&bulk_eot_timer);
-
- /* Set up the data structures for USB traffic. Note that this must be done before
- any interrupt that relies on sane DMA list occurrs. */
- init_rx_buffers();
- init_tx_bulk_ep();
- init_tx_ctrl_ep();
- init_tx_intr_ep();
- init_tx_isoc_ep();
-
- device_initialize(&fake_device);
- kobject_set_name(&fake_device.kobj, "etrax_usb");
- kobject_add(&fake_device.kobj);
- kobject_uevent(&fake_device.kobj, KOBJ_ADD);
- hc->bus->controller = &fake_device;
- usb_register_bus(hc->bus);
-
- *R_IRQ_MASK2_SET =
- /* Note that these interrupts are not used. */
- IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
- /* Sub channel 1 (ctrl) descr. interrupts are used. */
- IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
- IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
- /* Sub channel 3 (isoc) descr. interrupts are used. */
- IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
-
- /* Note that the dma9_descr interrupt is not used. */
- *R_IRQ_MASK2_SET =
- IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
- IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
-
- /* FIXME: Enable iso_eof only when isoc traffic is running. */
- *R_USB_IRQ_MASK_SET =
- IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
- IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
- IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
- IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
- IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
-
-
- if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
- "ETRAX 100LX built-in USB (HC)", hc)) {
- err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
- etrax_usb_hc_cleanup();
- DBFEXIT;
- return -1;
- }
-
- if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0,
- "ETRAX 100LX built-in USB (Rx)", hc)) {
- err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ);
- etrax_usb_hc_cleanup();
- DBFEXIT;
- return -1;
- }
-
- if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0,
- "ETRAX 100LX built-in USB (Tx)", hc)) {
- err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ);
- etrax_usb_hc_cleanup();
- DBFEXIT;
- return -1;
- }
-
- /* R_USB_COMMAND:
- USB commands in host mode. The fields in this register should all be
- written to in one write. Do not read-modify-write one field at a time. A
- write to this register will trigger events in the USB controller and an
- incomplete command may lead to unpredictable results, and in worst case
- even to a deadlock in the controller.
- (Note however that the busy field is read-only, so no need to write to it.) */
-
- /* Check the busy bit before writing to R_USB_COMMAND. */
-
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* Reset the USB interface. */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, nop) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
-
- /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
- to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
- allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
-
- While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
- behaviour, it doesn't solve this problem. What happens is that a control transfer will not
- be interrupted in its data stage when PSTART happens (the point at which periodic traffic
- is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
- PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
- there may be too little time left for an isochronous transfer, causing an epid attention
- interrupt due to perror. The work-around for this is to let the control transfers run at the
- end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
- fit into the frame. However, since there will *always* be a control transfer at the beginning
- of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
- which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
- this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
- sure that the periodic transfers that are inserted will always fit in the frame.
-
- The idea was suggested that a control transfer could be split up into several 8 byte transfers,
- so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
- hasn't been implemented.
-
- The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
- for possible bit stuffing. */
-
- *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
-
-#ifdef CONFIG_ETRAX_USB_HOST_PORT1
- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
-#endif
-
-#ifdef CONFIG_ETRAX_USB_HOST_PORT2
- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
-#endif
-
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* Configure the USB interface as a host controller. */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, nop) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
-
- /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
- sequence of resetting the ports. If we reset both ports now, and there are devices
- on both ports, we will get a bus error because both devices will answer the set address
- request. */
-
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- /* Start processing of USB traffic. */
- *R_USB_COMMAND =
- IO_STATE(R_USB_COMMAND, port_sel, nop) |
- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-
- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-
- usb_rh = usb_alloc_dev(NULL, hc->bus, 0);
- hc->bus->root_hub = usb_rh;
- usb_rh->state = USB_STATE_ADDRESS;
- usb_rh->speed = USB_SPEED_FULL;
- usb_rh->devnum = 1;
- hc->bus->devnum_next = 2;
- usb_rh->ep0.desc.wMaxPacketSize = __const_cpu_to_le16(64);
- usb_get_device_descriptor(usb_rh, USB_DT_DEVICE_SIZE);
- usb_new_device(usb_rh);
-
- DBFEXIT;
-
- return 0;
-}
-
-static void etrax_usb_hc_cleanup(void)
-{
- DBFENTER;
-
- free_irq(ETRAX_USB_HC_IRQ, NULL);
- free_irq(ETRAX_USB_RX_IRQ, NULL);
- free_irq(ETRAX_USB_TX_IRQ, NULL);
-
- usb_deregister_bus(etrax_usb_bus);
-
- /* FIXME: call kmem_cache_destroy here? */
-
- DBFEXIT;
-}
-
-module_init(etrax_usb_hc_init);
-module_exit(etrax_usb_hc_cleanup);
diff --git a/drivers/usb/host/hc_crisv10.h b/drivers/usb/host/hc_crisv10.h
deleted file mode 100644
index 62f77111d418..000000000000
--- a/drivers/usb/host/hc_crisv10.h
+++ /dev/null
@@ -1,289 +0,0 @@
-#ifndef __LINUX_ETRAX_USB_H
-#define __LINUX_ETRAX_USB_H
-
-#include <linux/types.h>
-#include <linux/list.h>
-
-typedef struct USB_IN_Desc {
- volatile __u16 sw_len;
- volatile __u16 command;
- volatile unsigned long next;
- volatile unsigned long buf;
- volatile __u16 hw_len;
- volatile __u16 status;
-} USB_IN_Desc_t;
-
-typedef struct USB_SB_Desc {
- volatile __u16 sw_len;
- volatile __u16 command;
- volatile unsigned long next;
- volatile unsigned long buf;
- __u32 dummy;
-} USB_SB_Desc_t;
-
-typedef struct USB_EP_Desc {
- volatile __u16 hw_len;
- volatile __u16 command;
- volatile unsigned long sub;
- volatile unsigned long next;
- __u32 dummy;
-} USB_EP_Desc_t;
-
-struct virt_root_hub {
- int devnum;
- void *urb;
- void *int_addr;
- int send;
- int interval;
- int numports;
- struct timer_list rh_int_timer;
- volatile __u16 wPortChange_1;
- volatile __u16 wPortChange_2;
- volatile __u16 prev_wPortStatus_1;
- volatile __u16 prev_wPortStatus_2;
-};
-
-struct etrax_usb_intr_traffic {
- int sleeping;
- int error;
- struct wait_queue *wq;
-};
-
-typedef struct etrax_usb_hc {
- struct usb_bus *bus;
- struct virt_root_hub rh;
- struct etrax_usb_intr_traffic intr;
-} etrax_hc_t;
-
-typedef enum {
- STARTED,
- NOT_STARTED,
- UNLINK,
- TRANSFER_DONE,
- WAITING_FOR_DESCR_INTR
-} etrax_usb_urb_state_t;
-
-
-
-typedef struct etrax_usb_urb_priv {
- /* The first_sb field is used for freeing all SB descriptors belonging
- to an urb. The corresponding ep descriptor's sub pointer cannot be
- used for this since the DMA advances the sub pointer as it processes
- the sb list. */
- USB_SB_Desc_t *first_sb;
- /* The last_sb field referes to the last SB descriptor that belongs to
- this urb. This is important to know so we can free the SB descriptors
- that ranges between first_sb and last_sb. */
- USB_SB_Desc_t *last_sb;
-
- /* The rx_offset field is used in ctrl and bulk traffic to keep track
- of the offset in the urb's transfer_buffer where incoming data should be
- copied to. */
- __u32 rx_offset;
-
- /* Counter used in isochronous transfers to keep track of the
- number of packets received/transmitted. */
- __u32 isoc_packet_counter;
-
- /* This field is used to pass information about the urb's current state between
- the various interrupt handlers (thus marked volatile). */
- volatile etrax_usb_urb_state_t urb_state;
-
- /* Connection between the submitted urb and ETRAX epid number */
- __u8 epid;
-
- /* The rx_data_list field is used for periodic traffic, to hold
- received data for later processing in the the complete_urb functions,
- where the data us copied to the urb's transfer_buffer. Basically, we
- use this intermediate storage because we don't know when it's safe to
- reuse the transfer_buffer (FIXME?). */
- struct list_head rx_data_list;
-} etrax_urb_priv_t;
-
-/* This struct is for passing data from the top half to the bottom half. */
-typedef struct usb_interrupt_registers
-{
- etrax_hc_t *hc;
- __u32 r_usb_epid_attn;
- __u8 r_usb_status;
- __u16 r_usb_rh_port_status_1;
- __u16 r_usb_rh_port_status_2;
- __u32 r_usb_irq_mask_read;
- __u32 r_usb_fm_number;
- struct work_struct usb_bh;
-} usb_interrupt_registers_t;
-
-/* This struct is for passing data from the isoc top half to the isoc bottom half. */
-typedef struct usb_isoc_complete_data
-{
- struct urb *urb;
- struct work_struct usb_bh;
-} usb_isoc_complete_data_t;
-
-/* This struct holds data we get from the rx descriptors for DMA channel 9
- for periodic traffic (intr and isoc). */
-typedef struct rx_data
-{
- void *data;
- int length;
- struct list_head list;
-} rx_data_t;
-
-typedef struct urb_entry
-{
- struct urb *urb;
- struct list_head list;
-} urb_entry_t;
-
-/* ---------------------------------------------------------------------------
- Virtual Root HUB
- ------------------------------------------------------------------------- */
-/* destination of request */
-#define RH_INTERFACE 0x01
-#define RH_ENDPOINT 0x02
-#define RH_OTHER 0x03
-
-#define RH_CLASS 0x20
-#define RH_VENDOR 0x40
-
-/* Requests: bRequest << 8 | bmRequestType */
-#define RH_GET_STATUS 0x0080
-#define RH_CLEAR_FEATURE 0x0100
-#define RH_SET_FEATURE 0x0300
-#define RH_SET_ADDRESS 0x0500
-#define RH_GET_DESCRIPTOR 0x0680
-#define RH_SET_DESCRIPTOR 0x0700
-#define RH_GET_CONFIGURATION 0x0880
-#define RH_SET_CONFIGURATION 0x0900
-#define RH_GET_STATE 0x0280
-#define RH_GET_INTERFACE 0x0A80
-#define RH_SET_INTERFACE 0x0B00
-#define RH_SYNC_FRAME 0x0C80
-/* Our Vendor Specific Request */
-#define RH_SET_EP 0x2000
-
-
-/* Hub port features */
-#define RH_PORT_CONNECTION 0x00
-#define RH_PORT_ENABLE 0x01
-#define RH_PORT_SUSPEND 0x02
-#define RH_PORT_OVER_CURRENT 0x03
-#define RH_PORT_RESET 0x04
-#define RH_PORT_POWER 0x08
-#define RH_PORT_LOW_SPEED 0x09
-#define RH_C_PORT_CONNECTION 0x10
-#define RH_C_PORT_ENABLE 0x11
-#define RH_C_PORT_SUSPEND 0x12
-#define RH_C_PORT_OVER_CURRENT 0x13
-#define RH_C_PORT_RESET 0x14
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER 0x00
-#define RH_C_HUB_OVER_CURRENT 0x01
-
-#define RH_DEVICE_REMOTE_WAKEUP 0x00
-#define RH_ENDPOINT_STALL 0x01
-
-/* Our Vendor Specific feature */
-#define RH_REMOVE_EP 0x00
-
-
-#define RH_ACK 0x01
-#define RH_REQ_ERR -1
-#define RH_NACK 0x00
-
-/* Field definitions for */
-
-#define USB_IN_command__eol__BITNR 0 /* command macros */
-#define USB_IN_command__eol__WIDTH 1
-#define USB_IN_command__eol__no 0
-#define USB_IN_command__eol__yes 1
-
-#define USB_IN_command__intr__BITNR 3
-#define USB_IN_command__intr__WIDTH 1
-#define USB_IN_command__intr__no 0
-#define USB_IN_command__intr__yes 1
-
-#define USB_IN_status__eop__BITNR 1 /* status macros. */
-#define USB_IN_status__eop__WIDTH 1
-#define USB_IN_status__eop__no 0
-#define USB_IN_status__eop__yes 1
-
-#define USB_IN_status__eot__BITNR 5
-#define USB_IN_status__eot__WIDTH 1
-#define USB_IN_status__eot__no 0
-#define USB_IN_status__eot__yes 1
-
-#define USB_IN_status__error__BITNR 6
-#define USB_IN_status__error__WIDTH 1
-#define USB_IN_status__error__no 0
-#define USB_IN_status__error__yes 1
-
-#define USB_IN_status__nodata__BITNR 7
-#define USB_IN_status__nodata__WIDTH 1
-#define USB_IN_status__nodata__no 0
-#define USB_IN_status__nodata__yes 1
-
-#define USB_IN_status__epid__BITNR 8
-#define USB_IN_status__epid__WIDTH 5
-
-#define USB_EP_command__eol__BITNR 0
-#define USB_EP_command__eol__WIDTH 1
-#define USB_EP_command__eol__no 0
-#define USB_EP_command__eol__yes 1
-
-#define USB_EP_command__eof__BITNR 1
-#define USB_EP_command__eof__WIDTH 1
-#define USB_EP_command__eof__no 0
-#define USB_EP_command__eof__yes 1
-
-#define USB_EP_command__intr__BITNR 3
-#define USB_EP_command__intr__WIDTH 1
-#define USB_EP_command__intr__no 0
-#define USB_EP_command__intr__yes 1
-
-#define USB_EP_command__enable__BITNR 4
-#define USB_EP_command__enable__WIDTH 1
-#define USB_EP_command__enable__no 0
-#define USB_EP_command__enable__yes 1
-
-#define USB_EP_command__hw_valid__BITNR 5
-#define USB_EP_command__hw_valid__WIDTH 1
-#define USB_EP_command__hw_valid__no 0
-#define USB_EP_command__hw_valid__yes 1
-
-#define USB_EP_command__epid__BITNR 8
-#define USB_EP_command__epid__WIDTH 5
-
-#define USB_SB_command__eol__BITNR 0 /* command macros. */
-#define USB_SB_command__eol__WIDTH 1
-#define USB_SB_command__eol__no 0
-#define USB_SB_command__eol__yes 1
-
-#define USB_SB_command__eot__BITNR 1
-#define USB_SB_command__eot__WIDTH 1
-#define USB_SB_command__eot__no 0
-#define USB_SB_command__eot__yes 1
-
-#define USB_SB_command__intr__BITNR 3
-#define USB_SB_command__intr__WIDTH 1
-#define USB_SB_command__intr__no 0
-#define USB_SB_command__intr__yes 1
-
-#define USB_SB_command__tt__BITNR 4
-#define USB_SB_command__tt__WIDTH 2
-#define USB_SB_command__tt__zout 0
-#define USB_SB_command__tt__in 1
-#define USB_SB_command__tt__out 2
-#define USB_SB_command__tt__setup 3
-
-
-#define USB_SB_command__rem__BITNR 8
-#define USB_SB_command__rem__WIDTH 6
-
-#define USB_SB_command__full__BITNR 6
-#define USB_SB_command__full__WIDTH 1
-#define USB_SB_command__full__no 0
-#define USB_SB_command__full__yes 1
-
-#endif
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index f0d29eda3c6d..e8bbe8bc2598 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -486,9 +486,6 @@ static int ohci_run (struct ohci_hcd *ohci)
* or if bus glue did the same (e.g. for PCI add-in cards with
* PCI PM support).
*/
- ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
- hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
- ohci_readl (ohci, &ohci->regs->control));
if ((ohci->hc_control & OHCI_CTRL_RWC) != 0
&& !device_may_wakeup(hcd->self.controller))
device_init_wakeup(hcd->self.controller, 1);
@@ -744,9 +741,6 @@ static void ohci_stop (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
- ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n",
- hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
- hcd->state);
ohci_dump (ohci, 1);
flush_scheduled_work();
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index b331ac4d0d62..79705609fd0c 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -20,10 +20,16 @@
/*-------------------------------------------------------------------------*/
+static int broken_suspend(struct usb_hcd *hcd)
+{
+ device_init_wakeup(&hcd->self.root_hub->dev, 0);
+ return 0;
+}
+
/* AMD 756, for most chips (early revs), corrupts register
* values on read ... so enable the vendor workaround.
*/
-static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd)
+static int ohci_quirk_amd756(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -31,16 +37,14 @@ static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd)
ohci_dbg (ohci, "AMD756 erratum 4 workaround\n");
/* also erratum 10 (suspend/resume issues) */
- device_init_wakeup(&hcd->self.root_hub->dev, 0);
-
- return 0;
+ return broken_suspend(hcd);
}
/* Apple's OHCI driver has a lot of bizarre workarounds
* for this chip. Evidently control and bulk lists
* can get confused. (B&W G3 models, and ...)
*/
-static int __devinit ohci_quirk_opti(struct usb_hcd *hcd)
+static int ohci_quirk_opti(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -53,7 +57,7 @@ static int __devinit ohci_quirk_opti(struct usb_hcd *hcd)
* identify the USB (fn2). This quirk might apply to more or
* even all NSC stuff.
*/
-static int __devinit ohci_quirk_ns(struct usb_hcd *hcd)
+static int ohci_quirk_ns(struct usb_hcd *hcd)
{
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
struct pci_dev *b;
@@ -75,7 +79,7 @@ static int __devinit ohci_quirk_ns(struct usb_hcd *hcd)
* delays before control or bulk queues get re-activated
* in finish_unlinks()
*/
-static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd)
+static int ohci_quirk_zfmicro(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -88,7 +92,7 @@ static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd)
/* Check for Toshiba SCC OHCI which has big endian registers
* and little endian in memory data structures
*/
-static int __devinit ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
+static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -129,6 +133,18 @@ static const struct pci_device_id ohci_pci_quirks[] = {
PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6),
.driver_data = (unsigned long)ohci_quirk_toshiba_scc,
},
+ {
+ /* Toshiba portege 4000 */
+ .vendor = PCI_VENDOR_ID_AL,
+ .device = 0x5237,
+ .subvendor = PCI_VENDOR_ID_TOSHIBA_2,
+ .subdevice = 0x0004,
+ .driver_data = (unsigned long) broken_suspend,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
+ .driver_data = (unsigned long) broken_suspend,
+ },
/* FIXME for some of the early AMD 760 southbridges, OHCI
* won't work at all. blacklist them.
*/
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 19a0cc02b9a2..4aed305982ec 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -123,10 +123,14 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
{
- if (!list_empty(&td->list))
+ if (!list_empty(&td->list)) {
dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
- if (!list_empty(&td->fl_list))
+ WARN_ON(1);
+ }
+ if (!list_empty(&td->fl_list)) {
dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+ WARN_ON(1);
+ }
dma_pool_free(uhci->td_pool, td, td->dma_handle);
}
@@ -291,8 +295,10 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);
- if (!list_empty(&qh->queue))
+ if (!list_empty(&qh->queue)) {
dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
+ WARN_ON(1);
+ }
list_del(&qh->node);
if (qh->udev) {
@@ -740,9 +746,11 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci,
{
struct uhci_td *td, *tmp;
- if (!list_empty(&urbp->node))
+ if (!list_empty(&urbp->node)) {
dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n",
urbp->urb);
+ WARN_ON(1);
+ }
list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
uhci_remove_td_from_urbp(td);
diff --git a/drivers/usb/input/ati_remote2.c b/drivers/usb/input/ati_remote2.c
index 83f1f79db7c7..6459be90599c 100644
--- a/drivers/usb/input/ati_remote2.c
+++ b/drivers/usb/input/ati_remote2.c
@@ -2,6 +2,7 @@
* ati_remote2 - ATI/Philips USB RF remote driver
*
* Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi>
+ * Copyright (C) 2007 Peter Stokes <linux@dadeos.freeserve.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -11,13 +12,29 @@
#include <linux/usb/input.h>
#define DRIVER_DESC "ATI/Philips USB RF remote driver"
-#define DRIVER_VERSION "0.1"
+#define DRIVER_VERSION "0.2"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
MODULE_LICENSE("GPL");
+/*
+ * ATI Remote Wonder II Channel Configuration
+ *
+ * The remote control can by assigned one of sixteen "channels" in order to facilitate
+ * the use of multiple remote controls within range of each other.
+ * A remote's "channel" may be altered by pressing and holding the "PC" button for
+ * approximately 3 seconds, after which the button will slowly flash the count of the
+ * currently configured "channel", using the numeric keypad enter a number between 1 and
+ * 16 and then the "PC" button again, the button will slowly flash the count of the
+ * newly configured "channel".
+ */
+
+static unsigned int channel_mask = 0xFFFF;
+module_param(channel_mask, uint, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
+
static unsigned int mode_mask = 0x1F;
module_param(mode_mask, uint, 0644);
MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
@@ -146,15 +163,23 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
{
struct input_dev *idev = ar2->idev;
u8 *data = ar2->buf[0];
+ int channel, mode;
+
+ channel = data[0] >> 4;
+
+ if (!((1 << channel) & channel_mask))
+ return;
- if (data[0] > 4) {
+ mode = data[0] & 0x0F;
+
+ if (mode > 4) {
dev_err(&ar2->intf[0]->dev,
"Unknown mode byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
- if (!((1 << data[0]) & mode_mask))
+ if (!((1 << mode) & mode_mask))
return;
input_event(idev, EV_REL, REL_X, (s8) data[1]);
@@ -177,9 +202,16 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
{
struct input_dev *idev = ar2->idev;
u8 *data = ar2->buf[1];
- int hw_code, index;
+ int channel, mode, hw_code, index;
+
+ channel = data[0] >> 4;
+
+ if (!((1 << channel) & channel_mask))
+ return;
- if (data[0] > 4) {
+ mode = data[0] & 0x0F;
+
+ if (mode > 4) {
dev_err(&ar2->intf[1]->dev,
"Unknown mode byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
@@ -199,16 +231,16 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
* events for the mouse pad so we filter out any subsequent
* events from the same mode key.
*/
- if (ar2->mode == data[0])
+ if (ar2->mode == mode)
return;
if (data[1] == 0)
- ar2->mode = data[0];
+ ar2->mode = mode;
- hw_code |= data[0] << 8;
+ hw_code |= mode << 8;
}
- if (!((1 << data[0]) & mode_mask))
+ if (!((1 << mode) & mode_mask))
return;
index = ati_remote2_lookup(hw_code);
@@ -379,6 +411,41 @@ static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
}
}
+static int ati_remote2_setup(struct ati_remote2 *ar2)
+{
+ int r, i, channel;
+
+ /*
+ * Configure receiver to only accept input from remote "channel"
+ * channel == 0 -> Accept input from any remote channel
+ * channel == 1 -> Only accept input from remote channel 1
+ * channel == 2 -> Only accept input from remote channel 2
+ * ...
+ * channel == 16 -> Only accept input from remote channel 16
+ */
+
+ channel = 0;
+ for (i = 0; i < 16; i++) {
+ if ((1 << i) & channel_mask) {
+ if (!(~(1 << i) & 0xFFFF & channel_mask))
+ channel = i + 1;
+ break;
+ }
+ }
+
+ r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
+ 0x20,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (r) {
+ dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
+ __FUNCTION__, r);
+ return r;
+ }
+
+ return 0;
+}
+
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
@@ -409,6 +476,10 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
if (r)
goto fail2;
+ r = ati_remote2_setup(ar2);
+ if (r)
+ goto fail2;
+
usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
diff --git a/drivers/usb/input/gtco.c b/drivers/usb/input/gtco.c
index 203cdc1bbba4..ae756e0afc99 100644
--- a/drivers/usb/input/gtco.c
+++ b/drivers/usb/input/gtco.c
@@ -1047,13 +1047,10 @@ static void gtco_disconnect(struct usb_interface *interface)
/* Grab private device ptr */
struct gtco *device = usb_get_intfdata (interface);
- struct input_dev *inputdev;
-
- inputdev = device->inputdevice;
/* Now reverse all the registration stuff */
if (device) {
- input_unregister_device(inputdev);
+ input_unregister_device(device->inputdevice);
usb_kill_urb(device->urbinfo);
usb_free_urb(device->urbinfo);
usb_buffer_free(device->usbdev, REPORT_MAX_SIZE,
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 75bfab95ab3c..77145f9db043 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -285,23 +285,24 @@ static int adu_open(struct inode *inode, struct file *file)
/* 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 */
+ if (dev->open_count == 1) {
+ /* 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;
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ if (retval)
+ --dev->open_count;
+ }
up(&dev->sem);
exit_no_device:
@@ -469,7 +470,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
adu_interrupt_in_callback,
dev,
dev->interrupt_in_endpoint->bInterval);
- retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);
if (!retval) {
spin_unlock_irqrestore(&dev->buflock, flags);
dbg(2," %s : submitted OK", __FUNCTION__);
@@ -539,7 +540,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
size_t bytes_written = 0;
size_t bytes_to_write;
size_t buffer_size;
- int retval = 0;
+ int retval;
int timeout = 0;
dbg(2," %s : enter, count = %Zd", __FUNCTION__, count);
@@ -547,7 +548,9 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
dev = file->private_data;
/* lock this object */
- down_interruptible(&dev->sem);
+ retval = down_interruptible(&dev->sem);
+ if (retval)
+ goto exit_nolock;
/* verify that the device wasn't unplugged */
if (dev->udev == NULL || dev->minor == 0) {
@@ -575,7 +578,11 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
}
up(&dev->sem);
timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout);
- down_interruptible(&dev->sem);
+ retval = down_interruptible(&dev->sem);
+ if (retval) {
+ retval = bytes_written ? bytes_written : retval;
+ goto exit_nolock;
+ }
if (timeout > 0) {
break;
}
@@ -637,6 +644,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
exit:
/* unlock the device */
up(&dev->sem);
+exit_nolock:
dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index b63b5f34b2aa..d721380b242d 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -246,11 +246,13 @@ static void cypress_disconnect(struct usb_interface *interface)
struct cypress *dev;
dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
/* remove device attribute files */
device_remove_file(&interface->dev, &dev_attr_port0);
device_remove_file(&interface->dev, &dev_attr_port1);
+ /* the intfdata can be set to NULL only after the
+ * device files have been removed */
+ usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index bc3327e3dd78..e2172e5cf152 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -2304,7 +2304,6 @@ static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi)
#define OHCI_QUIRK_SUPERIO 0x02
#define OHCI_QUIRK_INITRESET 0x04
#define OHCI_BIG_ENDIAN 0x08
-#define OHCI_QUIRK_ZFMICRO 0x10
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
OHCI_INTR_WDH)
@@ -2910,24 +2909,28 @@ static int __init ftdi_elan_init(void)
INIT_LIST_HEAD(&ftdi_static_list);
status_queue = create_singlethread_workqueue("ftdi-status-control");
if (!status_queue)
- goto err1;
+ goto err_status_queue;
command_queue = create_singlethread_workqueue("ftdi-command-engine");
if (!command_queue)
- goto err2;
+ goto err_command_queue;
respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
if (!respond_queue)
- goto err3;
+ goto err_respond_queue;
result = usb_register(&ftdi_elan_driver);
- if (result)
+ if (result) {
+ destroy_workqueue(status_queue);
+ destroy_workqueue(command_queue);
+ destroy_workqueue(respond_queue);
printk(KERN_ERR "usb_register failed. Error number %d\n",
result);
+ }
return result;
- err3:
+ err_respond_queue:
destroy_workqueue(command_queue);
- err2:
+ err_command_queue:
destroy_workqueue(status_queue);
- err1:
+ err_status_queue:
printk(KERN_ERR "%s couldn't create workqueue\n", ftdi_elan_driver.name);
return -ENOMEM;
}
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index d69665c8de02..fc51207b71b8 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -118,7 +118,7 @@ static int usb_get_report(struct usb_device *dev,
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, (type << 8) + id,
inter->desc.bInterfaceNumber, buf, size,
- GET_TIMEOUT);
+ GET_TIMEOUT*HZ);
}
//#endif
@@ -133,7 +133,7 @@ static int usb_set_report(struct usb_interface *intf, unsigned char type,
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(type << 8) + id,
intf->cur_altsetting->desc.bInterfaceNumber, buf,
- size, 1);
+ size, HZ);
}
/*---------------------*/
@@ -417,14 +417,14 @@ static ssize_t iowarrior_write(struct file *file,
if (!int_out_urb) {
retval = -ENOMEM;
dbg("%s Unable to allocate urb ", __func__);
- goto error;
+ goto error_no_urb;
}
buf = usb_buffer_alloc(dev->udev, dev->report_size,
GFP_KERNEL, &int_out_urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
dbg("%s Unable to allocate buffer ", __func__);
- goto error;
+ goto error_no_buffer;
}
usb_fill_int_urb(int_out_urb, dev->udev,
usb_sndintpipe(dev->udev,
@@ -459,7 +459,9 @@ static ssize_t iowarrior_write(struct file *file,
error:
usb_buffer_free(dev->udev, dev->report_size, buf,
int_out_urb->transfer_dma);
+error_no_buffer:
usb_free_urb(int_out_urb);
+error_no_urb:
atomic_dec(&dev->write_busy);
wake_up_interruptible(&dev->write_wait);
exit:
@@ -748,7 +750,6 @@ static int iowarrior_probe(struct usb_interface *interface,
struct usb_endpoint_descriptor *endpoint;
int i;
int retval = -ENOMEM;
- int idele = 0;
/* allocate memory for our device state and intialize it */
dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
@@ -824,11 +825,10 @@ static int iowarrior_probe(struct usb_interface *interface,
/* Set the idle timeout to 0, if this is interface 0 */
if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
- idele = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x0A,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
- 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
- dbg("idele = %d", idele);
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x0A,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
}
/* allow device read and ioctl */
dev->present = 1;
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 788a11e6772f..11555bde655b 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -62,6 +62,8 @@
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
+#define USB_VENDOR_ID_MICROCHIP 0x04d8
+#define USB_DEVICE_ID_PICDEM 0x000c
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define USB_LD_MINOR_BASE 0
@@ -89,6 +91,7 @@ static struct usb_device_id ld_usb_table [] = {
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
+ { USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICDEM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ld_usb_table);
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index ada2ebc464ae..887ef953f3d8 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -47,6 +47,7 @@ struct usb_lcd {
#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
static struct usb_driver lcd_driver;
+static DEFINE_MUTEX(usb_lcd_open_mutex);
static void lcd_delete(struct kref *kref)
@@ -68,6 +69,7 @@ static int lcd_open(struct inode *inode, struct file *file)
subminor = iminor(inode);
+ mutex_lock(&usb_lcd_open_mutex);
interface = usb_find_interface(&lcd_driver, subminor);
if (!interface) {
err ("USBLCD: %s - error, can't find device for minor %d",
@@ -89,6 +91,7 @@ static int lcd_open(struct inode *inode, struct file *file)
file->private_data = dev;
exit:
+ mutex_unlock(&usb_lcd_open_mutex);
return retval;
}
@@ -347,7 +350,7 @@ static void lcd_disconnect(struct usb_interface *interface)
int minor = interface->minor;
/* prevent skel_open() from racing skel_disconnect() */
- lock_kernel();
+ mutex_lock(&usb_lcd_open_mutex);
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
@@ -355,7 +358,7 @@ static void lcd_disconnect(struct usb_interface *interface)
/* give back our minor */
usb_deregister_dev(interface, &lcd_class);
- unlock_kernel();
+ mutex_unlock(&usb_lcd_open_mutex);
/* decrement our usage count */
kref_put(&dev->kref, lcd_delete);
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index b2bedd974ac3..0af11a66207c 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -356,8 +356,10 @@ static inline char mon_bin_get_setup(unsigned char *setupb,
if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
return '-';
- if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
+ if (urb->dev->bus->uses_dma &&
+ (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN);
+ }
if (urb->setup_packet == NULL)
return 'Z';
@@ -369,7 +371,8 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp,
unsigned int offset, struct urb *urb, unsigned int length)
{
- if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
+ if (urb->dev->bus->uses_dma &&
+ (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
mon_dmapeek_vec(rp, offset, urb->transfer_dma, length);
return 0;
}
@@ -440,7 +443,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
/* We use the fact that usb_pipein() returns 0x80 */
ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe);
ep->devnum = usb_pipedevice(urb->pipe);
- ep->busnum = rp->r.m_bus->u_bus->busnum;
+ ep->busnum = urb->dev->bus->busnum;
ep->id = (unsigned long) urb;
ep->ts_sec = ts.tv_sec;
ep->ts_usec = ts.tv_usec;
@@ -500,7 +503,7 @@ static void mon_bin_error(void *data, struct urb *urb, int error)
/* We use the fact that usb_pipein() returns 0x80 */
ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe);
ep->devnum = usb_pipedevice(urb->pipe);
- ep->busnum = rp->r.m_bus->u_bus->busnum;
+ ep->busnum = urb->dev->bus->busnum;
ep->id = (unsigned long) urb;
ep->status = error;
@@ -515,7 +518,6 @@ static void mon_bin_error(void *data, struct urb *urb, int error)
static int mon_bin_open(struct inode *inode, struct file *file)
{
struct mon_bus *mbus;
- struct usb_bus *ubus;
struct mon_reader_bin *rp;
size_t size;
int rc;
@@ -525,7 +527,7 @@ static int mon_bin_open(struct inode *inode, struct file *file)
mutex_unlock(&mon_lock);
return -ENODEV;
}
- if ((ubus = mbus->u_bus) == NULL) {
+ if (mbus != &mon_bus0 && mbus->u_bus == NULL) {
printk(KERN_ERR TAG ": consistency error on open\n");
mutex_unlock(&mon_lock);
return -ENODEV;
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index c9739e7b35e5..8a1df2c9c73e 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -16,8 +16,6 @@
#include "usb_mon.h"
#include "../core/hcd.h"
-static void mon_submit(struct usb_bus *ubus, struct urb *urb);
-static void mon_complete(struct usb_bus *ubus, struct urb *urb);
static void mon_stop(struct mon_bus *mbus);
static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
static void mon_bus_drop(struct kref *r);
@@ -25,6 +23,7 @@ static void mon_bus_init(struct usb_bus *ubus);
DEFINE_MUTEX(mon_lock);
+struct mon_bus mon_bus0; /* Pseudo bus meaning "all buses" */
static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
/*
@@ -35,22 +34,19 @@ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r)
{
unsigned long flags;
- struct usb_bus *ubus;
+ struct list_head *p;
spin_lock_irqsave(&mbus->lock, flags);
if (mbus->nreaders == 0) {
- ubus = mbus->u_bus;
- if (ubus->monitored) {
- /*
- * Something is really broken, refuse to go on and
- * possibly corrupt ops pointers or worse.
- */
- printk(KERN_ERR TAG ": bus %d is already monitored\n",
- ubus->busnum);
- spin_unlock_irqrestore(&mbus->lock, flags);
- return;
+ if (mbus == &mon_bus0) {
+ list_for_each (p, &mon_buses) {
+ struct mon_bus *m1;
+ m1 = list_entry(p, struct mon_bus, bus_link);
+ m1->u_bus->monitored = 1;
+ }
+ } else {
+ mbus->u_bus->monitored = 1;
}
- ubus->monitored = 1;
}
mbus->nreaders++;
list_add_tail(&r->r_link, &mbus->r_list);
@@ -80,77 +76,79 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r)
/*
*/
-static void mon_submit(struct usb_bus *ubus, struct urb *urb)
+static void mon_bus_submit(struct mon_bus *mbus, struct urb *urb)
{
- struct mon_bus *mbus;
unsigned long flags;
struct list_head *pos;
struct mon_reader *r;
- mbus = ubus->mon_bus;
- if (mbus == NULL)
- goto out_unlocked;
-
spin_lock_irqsave(&mbus->lock, flags);
- if (mbus->nreaders == 0)
- goto out_locked;
-
mbus->cnt_events++;
list_for_each (pos, &mbus->r_list) {
r = list_entry(pos, struct mon_reader, r_link);
r->rnf_submit(r->r_data, urb);
}
-
spin_unlock_irqrestore(&mbus->lock, flags);
return;
+}
-out_locked:
- spin_unlock_irqrestore(&mbus->lock, flags);
-out_unlocked:
- return;
+static void mon_submit(struct usb_bus *ubus, struct urb *urb)
+{
+ struct mon_bus *mbus;
+
+ if ((mbus = ubus->mon_bus) != NULL)
+ mon_bus_submit(mbus, urb);
+ mon_bus_submit(&mon_bus0, urb);
}
/*
*/
-static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error)
+static void mon_bus_submit_error(struct mon_bus *mbus, struct urb *urb, int error)
{
- struct mon_bus *mbus;
unsigned long flags;
struct list_head *pos;
struct mon_reader *r;
- mbus = ubus->mon_bus;
- if (mbus == NULL)
- goto out_unlocked;
-
spin_lock_irqsave(&mbus->lock, flags);
- if (mbus->nreaders == 0)
- goto out_locked;
-
mbus->cnt_events++;
list_for_each (pos, &mbus->r_list) {
r = list_entry(pos, struct mon_reader, r_link);
r->rnf_error(r->r_data, urb, error);
}
-
spin_unlock_irqrestore(&mbus->lock, flags);
return;
+}
-out_locked:
- spin_unlock_irqrestore(&mbus->lock, flags);
-out_unlocked:
- return;
+static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error)
+{
+ struct mon_bus *mbus;
+
+ if ((mbus = ubus->mon_bus) != NULL)
+ mon_bus_submit_error(mbus, urb, error);
+ mon_bus_submit_error(&mon_bus0, urb, error);
}
/*
*/
-static void mon_complete(struct usb_bus *ubus, struct urb *urb)
+static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb)
{
- struct mon_bus *mbus;
unsigned long flags;
struct list_head *pos;
struct mon_reader *r;
+ spin_lock_irqsave(&mbus->lock, flags);
+ mbus->cnt_events++;
+ list_for_each (pos, &mbus->r_list) {
+ r = list_entry(pos, struct mon_reader, r_link);
+ r->rnf_complete(r->r_data, urb);
+ }
+ spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+static void mon_complete(struct usb_bus *ubus, struct urb *urb)
+{
+ struct mon_bus *mbus;
+
mbus = ubus->mon_bus;
if (mbus == NULL) {
/*
@@ -162,13 +160,8 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb)
return;
}
- spin_lock_irqsave(&mbus->lock, flags);
- mbus->cnt_events++;
- list_for_each (pos, &mbus->r_list) {
- r = list_entry(pos, struct mon_reader, r_link);
- r->rnf_complete(r->r_data, urb);
- }
- spin_unlock_irqrestore(&mbus->lock, flags);
+ mon_bus_complete(mbus, urb);
+ mon_bus_complete(&mon_bus0, urb);
}
/* int (*unlink_urb) (struct urb *urb, int status); */
@@ -179,14 +172,26 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb)
static void mon_stop(struct mon_bus *mbus)
{
struct usb_bus *ubus = mbus->u_bus;
+ struct list_head *p;
- /*
- * A stop can be called for a dissolved mon_bus in case of
- * a reader staying across an rmmod foo_hcd.
- */
- if (ubus != NULL) {
- ubus->monitored = 0;
- mb();
+ if (mbus == &mon_bus0) {
+ list_for_each (p, &mon_buses) {
+ mbus = list_entry(p, struct mon_bus, bus_link);
+ /*
+ * We do not change nreaders here, so rely on mon_lock.
+ */
+ if (mbus->nreaders == 0 && (ubus = mbus->u_bus) != NULL)
+ ubus->monitored = 0;
+ }
+ } else {
+ /*
+ * A stop can be called for a dissolved mon_bus in case of
+ * a reader staying across an rmmod foo_hcd, so test ->u_bus.
+ */
+ if (mon_bus0.nreaders == 0 && (ubus = mbus->u_bus) != NULL) {
+ ubus->monitored = 0;
+ mb();
+ }
}
}
@@ -199,6 +204,10 @@ static void mon_stop(struct mon_bus *mbus)
static void mon_bus_add(struct usb_bus *ubus)
{
mon_bus_init(ubus);
+ mutex_lock(&mon_lock);
+ if (mon_bus0.nreaders != 0)
+ ubus->monitored = 1;
+ mutex_unlock(&mon_lock);
}
/*
@@ -250,12 +259,7 @@ static struct usb_mon_operations mon_ops_0 = {
static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
{
- /*
- * Never happens, but...
- */
if (ubus->monitored) {
- printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n",
- ubus->busnum);
ubus->monitored = 0;
mb();
}
@@ -263,6 +267,8 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
ubus->mon_bus = NULL;
mbus->u_bus = NULL;
mb();
+
+ /* We want synchronize_irq() here, but that needs an argument. */
}
/*
@@ -295,9 +301,8 @@ static void mon_bus_init(struct usb_bus *ubus)
*/
mbus->u_bus = ubus;
ubus->mon_bus = mbus;
- mbus->uses_dma = ubus->uses_dma;
- mbus->text_inited = mon_text_add(mbus, ubus);
+ mbus->text_inited = mon_text_add(mbus, ubus->busnum);
// mon_bin_add(...)
mutex_lock(&mon_lock);
@@ -309,6 +314,18 @@ err_alloc:
return;
}
+static void mon_bus0_init(void)
+{
+ struct mon_bus *mbus = &mon_bus0;
+
+ kref_init(&mbus->ref);
+ spin_lock_init(&mbus->lock);
+ INIT_LIST_HEAD(&mbus->r_list);
+
+ mbus->text_inited = mon_text_add(mbus, 0);
+ // mbus->bin_inited = mon_bin_add(mbus, 0);
+}
+
/*
* Search a USB bus by number. Notice that USB bus numbers start from one,
* which we may later use to identify "all" with zero.
@@ -322,6 +339,9 @@ struct mon_bus *mon_bus_lookup(unsigned int num)
struct list_head *p;
struct mon_bus *mbus;
+ if (num == 0) {
+ return &mon_bus0;
+ }
list_for_each (p, &mon_buses) {
mbus = list_entry(p, struct mon_bus, bus_link);
if (mbus->u_bus->busnum == num) {
@@ -341,6 +361,8 @@ static int __init mon_init(void)
if ((rc = mon_bin_init()) != 0)
goto err_bin;
+ mon_bus0_init();
+
if (usb_mon_register(&mon_ops_0) != 0) {
printk(KERN_NOTICE TAG ": unable to register with the core\n");
rc = -ENODEV;
@@ -374,6 +396,7 @@ static void __exit mon_exit(void)
usb_mon_deregister();
mutex_lock(&mon_lock);
+
while (!list_empty(&mon_buses)) {
p = mon_buses.next;
mbus = list_entry(p, struct mon_bus, bus_link);
@@ -397,6 +420,11 @@ static void __exit mon_exit(void)
mon_dissolve(mbus, mbus->u_bus);
kref_put(&mbus->ref, mon_bus_drop);
}
+
+ mbus = &mon_bus0;
+ if (mbus->text_inited)
+ mon_text_del(mbus);
+
mutex_unlock(&mon_lock);
mon_text_exit();
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 494ee3b9a226..ec0cc51e39ac 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -31,9 +31,21 @@
* to a local DoS. But we have to keep to root in order to prevent
* password sniffing from HID devices.
*/
-#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text))
+#define EVENT_MAX (4*PAGE_SIZE / sizeof(struct mon_event_text))
-#define PRINTF_DFL 160
+/*
+ * Potentially unlimited number; we limit it for similar allocations.
+ * The usbfs limits this to 128, but we're not quite as generous.
+ */
+#define ISODESC_MAX 5
+
+#define PRINTF_DFL 250 /* with 5 ISOs segs */
+
+struct mon_iso_desc {
+ int status;
+ unsigned int offset;
+ unsigned int length; /* Unsigned here, signed in URB. Historic. */
+};
struct mon_event_text {
struct list_head e_link;
@@ -41,10 +53,16 @@ struct mon_event_text {
unsigned int pipe; /* Pipe */
unsigned long id; /* From pointer, most of the time */
unsigned int tstamp;
+ int busnum;
int length; /* Depends on type: xfer length or act length */
int status;
+ int interval;
+ int start_frame;
+ int error_count;
char setup_flag;
char data_flag;
+ int numdesc; /* Full number */
+ struct mon_iso_desc isodesc[ISODESC_MAX];
unsigned char setup[SETUP_MAX];
unsigned char data[DATA_MAX];
};
@@ -68,6 +86,28 @@ static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */
static void mon_text_ctor(void *, struct kmem_cache *, unsigned long);
+struct mon_text_ptr {
+ int cnt, limit;
+ char *pbuf;
+};
+
+static struct mon_event_text *
+ mon_text_read_wait(struct mon_reader_text *rp, struct file *file);
+static void mon_text_read_head_t(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_head_u(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_statset(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_intstat(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_isostat(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_isodesc(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+static void mon_text_read_data(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep);
+
/*
* mon_text_submit
* mon_text_complete
@@ -84,8 +124,10 @@ static inline char mon_text_get_setup(struct mon_event_text *ep,
if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
return '-';
- if (mbus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+ if (urb->dev->bus->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. */
@@ -104,10 +146,10 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
len = DATA_MAX;
if (usb_pipein(pipe)) {
- if (ev_type == 'S')
+ if (ev_type != 'C')
return '<';
} else {
- if (ev_type == 'C')
+ if (ev_type != 'S')
return '>';
}
@@ -120,8 +162,10 @@ 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 (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+ if (urb->dev->bus->uses_dma &&
+ (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
return mon_dmapeek(ep->data, urb->transfer_dma, len);
+ }
if (urb->transfer_buffer == NULL)
return 'Z'; /* '0' would be not as pretty. */
@@ -146,6 +190,9 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
{
struct mon_event_text *ep;
unsigned int stamp;
+ struct usb_iso_packet_descriptor *fp;
+ struct mon_iso_desc *dp;
+ int i, ndesc;
stamp = mon_get_timestamp();
@@ -158,12 +205,36 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
ep->type = ev_type;
ep->pipe = urb->pipe;
ep->id = (unsigned long) urb;
+ ep->busnum = urb->dev->bus->busnum;
ep->tstamp = stamp;
ep->length = (ev_type == 'S') ?
urb->transfer_buffer_length : urb->actual_length;
/* Collecting status makes debugging sense for submits, too */
ep->status = urb->status;
+ if (usb_pipeint(urb->pipe)) {
+ ep->interval = urb->interval;
+ } else if (usb_pipeisoc(urb->pipe)) {
+ ep->interval = urb->interval;
+ ep->start_frame = urb->start_frame;
+ ep->error_count = urb->error_count;
+ }
+ ep->numdesc = urb->number_of_packets;
+ if (usb_pipeisoc(urb->pipe) && urb->number_of_packets > 0) {
+ if ((ndesc = urb->number_of_packets) > ISODESC_MAX)
+ ndesc = ISODESC_MAX;
+ fp = urb->iso_frame_desc;
+ dp = ep->isodesc;
+ for (i = 0; i < ndesc; i++) {
+ dp->status = fp->status;
+ dp->offset = fp->offset;
+ dp->length = (ev_type == 'S') ?
+ fp->length : fp->actual_length;
+ fp++;
+ dp++;
+ }
+ }
+
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);
@@ -199,6 +270,7 @@ static void mon_text_error(void *data, struct urb *urb, int error)
ep->type = 'E';
ep->pipe = urb->pipe;
ep->id = (unsigned long) urb;
+ ep->busnum = 0;
ep->tstamp = 0;
ep->length = 0;
ep->status = error;
@@ -237,13 +309,11 @@ static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
static int mon_text_open(struct inode *inode, struct file *file)
{
struct mon_bus *mbus;
- struct usb_bus *ubus;
struct mon_reader_text *rp;
int rc;
mutex_lock(&mon_lock);
mbus = inode->i_private;
- ubus = mbus->u_bus;
rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
if (rp == NULL) {
@@ -267,8 +337,7 @@ static int mon_text_open(struct inode *inode, struct file *file)
rp->r.rnf_error = mon_text_error;
rp->r.rnf_complete = mon_text_complete;
- snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
- (long)rp);
+ snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp);
rp->e_slab = kmem_cache_create(rp->slab_name,
sizeof(struct mon_event_text), sizeof(long), 0,
mon_text_ctor, NULL);
@@ -300,17 +369,75 @@ err_alloc:
* dd if=/dbg/usbmon/0t bs=10
* Also, we do not allow seeks and do not bother advancing the offset.
*/
-static ssize_t mon_text_read(struct file *file, char __user *buf,
+static ssize_t mon_text_read_t(struct file *file, char __user *buf,
size_t nbytes, loff_t *ppos)
{
struct mon_reader_text *rp = file->private_data;
+ struct mon_event_text *ep;
+ struct mon_text_ptr ptr;
+
+ if (IS_ERR(ep = mon_text_read_wait(rp, file)))
+ return PTR_ERR(ep);
+ mutex_lock(&rp->printf_lock);
+ ptr.cnt = 0;
+ ptr.pbuf = rp->printf_buf;
+ ptr.limit = rp->printf_size;
+
+ mon_text_read_head_t(rp, &ptr, ep);
+ mon_text_read_statset(rp, &ptr, ep);
+ ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ " %d", ep->length);
+ mon_text_read_data(rp, &ptr, ep);
+
+ if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
+ ptr.cnt = -EFAULT;
+ mutex_unlock(&rp->printf_lock);
+ kmem_cache_free(rp->e_slab, ep);
+ return ptr.cnt;
+}
+
+static ssize_t mon_text_read_u(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct mon_reader_text *rp = file->private_data;
+ struct mon_event_text *ep;
+ struct mon_text_ptr ptr;
+
+ if (IS_ERR(ep = mon_text_read_wait(rp, file)))
+ return PTR_ERR(ep);
+ mutex_lock(&rp->printf_lock);
+ ptr.cnt = 0;
+ ptr.pbuf = rp->printf_buf;
+ ptr.limit = rp->printf_size;
+
+ mon_text_read_head_u(rp, &ptr, ep);
+ if (ep->type == 'E') {
+ mon_text_read_statset(rp, &ptr, ep);
+ } else if (usb_pipeisoc(ep->pipe)) {
+ mon_text_read_isostat(rp, &ptr, ep);
+ mon_text_read_isodesc(rp, &ptr, ep);
+ } else if (usb_pipeint(ep->pipe)) {
+ mon_text_read_intstat(rp, &ptr, ep);
+ } else {
+ mon_text_read_statset(rp, &ptr, ep);
+ }
+ ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ " %d", ep->length);
+ mon_text_read_data(rp, &ptr, ep);
+
+ if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
+ ptr.cnt = -EFAULT;
+ mutex_unlock(&rp->printf_lock);
+ kmem_cache_free(rp->e_slab, ep);
+ return ptr.cnt;
+}
+
+static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,
+ struct file *file)
+{
struct mon_bus *mbus = rp->r.m_bus;
DECLARE_WAITQUEUE(waita, current);
struct mon_event_text *ep;
- int cnt, limit;
- char *pbuf;
- char udir, utype;
- int data_len, i;
add_wait_queue(&rp->wait, &waita);
set_current_state(TASK_INTERRUPTIBLE);
@@ -318,7 +445,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
if (file->f_flags & O_NONBLOCK) {
set_current_state(TASK_RUNNING);
remove_wait_queue(&rp->wait, &waita);
- return -EWOULDBLOCK; /* Same as EAGAIN in Linux */
+ return ERR_PTR(-EWOULDBLOCK);
}
/*
* We do not count nwaiters, because ->release is supposed
@@ -327,17 +454,19 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
schedule();
if (signal_pending(current)) {
remove_wait_queue(&rp->wait, &waita);
- return -EINTR;
+ return ERR_PTR(-EINTR);
}
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&rp->wait, &waita);
+ return ep;
+}
- mutex_lock(&rp->printf_lock);
- cnt = 0;
- pbuf = rp->printf_buf;
- limit = rp->printf_size;
+static void mon_text_read_head_t(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ char udir, utype;
udir = usb_pipein(ep->pipe) ? 'i' : 'o';
switch (usb_pipetype(ep->pipe)) {
@@ -346,13 +475,38 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
case PIPE_CONTROL: utype = 'C'; break;
default: /* PIPE_BULK */ utype = 'B';
}
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
"%lx %u %c %c%c:%03u:%02u",
ep->id, ep->tstamp, ep->type,
- utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+ utype, udir,
+ usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+}
+
+static void mon_text_read_head_u(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ char udir, utype;
+
+ udir = usb_pipein(ep->pipe) ? 'i' : 'o';
+ switch (usb_pipetype(ep->pipe)) {
+ case PIPE_ISOCHRONOUS: utype = 'Z'; break;
+ case PIPE_INTERRUPT: utype = 'I'; break;
+ case PIPE_CONTROL: utype = 'C'; break;
+ default: /* PIPE_BULK */ utype = 'B';
+ }
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ "%lx %u %c %c%c:%d:%03u:%u",
+ ep->id, ep->tstamp, ep->type,
+ utype, udir,
+ ep->busnum, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe));
+}
+
+static void mon_text_read_statset(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
if (ep->setup_flag == 0) { /* Setup packet is present and captured */
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" s %02x %02x %04x %04x %04x",
ep->setup[0],
ep->setup[1],
@@ -360,40 +514,86 @@ static ssize_t mon_text_read(struct file *file, char __user *buf,
(ep->setup[5] << 8) | ep->setup[4],
(ep->setup[7] << 8) | ep->setup[6]);
} else if (ep->setup_flag != '-') { /* Unable to capture setup packet */
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %c __ __ ____ ____ ____", ep->setup_flag);
} else { /* No setup for this kind of URB */
- cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status);
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d", ep->status);
}
- cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length);
+}
+
+static void mon_text_read_intstat(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d:%d", ep->status, ep->interval);
+}
+
+static void mon_text_read_isostat(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ if (ep->type == 'S') {
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d:%d:%d", ep->status, ep->interval, ep->start_frame);
+ } else {
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d:%d:%d:%d",
+ ep->status, ep->interval, ep->start_frame, ep->error_count);
+ }
+}
+
+static void mon_text_read_isodesc(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ int ndesc; /* Display this many */
+ int i;
+ const struct mon_iso_desc *dp;
+
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d", ep->numdesc);
+ ndesc = ep->numdesc;
+ if (ndesc > ISODESC_MAX)
+ ndesc = ISODESC_MAX;
+ if (ndesc < 0)
+ ndesc = 0;
+ dp = ep->isodesc;
+ for (i = 0; i < ndesc; i++) {
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " %d:%u:%u", dp->status, dp->offset, dp->length);
+ dp++;
+ }
+}
+
+static void mon_text_read_data(struct mon_reader_text *rp,
+ struct mon_text_ptr *p, const struct mon_event_text *ep)
+{
+ int data_len, i;
if ((data_len = ep->length) > 0) {
if (ep->data_flag == 0) {
- cnt += snprintf(pbuf + cnt, limit - cnt, " =");
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ " =");
if (data_len >= DATA_MAX)
data_len = DATA_MAX;
for (i = 0; i < data_len; i++) {
if (i % 4 == 0) {
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt,
+ p->limit - p->cnt,
" ");
}
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt,
+ p->limit - p->cnt,
"%02x", ep->data[i]);
}
- cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ "\n");
} else {
- cnt += snprintf(pbuf + cnt, limit - cnt,
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %c\n", ep->data_flag);
}
} else {
- cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+ p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
}
-
- if (copy_to_user(buf, rp->printf_buf, cnt))
- cnt = -EFAULT;
- mutex_unlock(&rp->printf_lock);
- kmem_cache_free(rp->e_slab, ep);
- return cnt;
}
static int mon_text_release(struct inode *inode, struct file *file)
@@ -439,34 +639,46 @@ static int mon_text_release(struct inode *inode, struct file *file)
return 0;
}
-static const struct file_operations mon_fops_text = {
+static const struct file_operations mon_fops_text_t = {
.owner = THIS_MODULE,
.open = mon_text_open,
.llseek = no_llseek,
- .read = mon_text_read,
- /* .write = mon_text_write, */
- /* .poll = mon_text_poll, */
- /* .ioctl = mon_text_ioctl, */
+ .read = mon_text_read_t,
.release = mon_text_release,
};
-int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
+static const struct file_operations mon_fops_text_u = {
+ .owner = THIS_MODULE,
+ .open = mon_text_open,
+ .llseek = no_llseek,
+ .read = mon_text_read_u,
+ .release = mon_text_release,
+};
+
+int mon_text_add(struct mon_bus *mbus, int busnum)
{
struct dentry *d;
enum { NAMESZ = 10 };
char name[NAMESZ];
int rc;
- rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
+ rc = snprintf(name, NAMESZ, "%dt", busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_t;
- d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text);
+ d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t);
if (d == NULL)
goto err_create_t;
mbus->dent_t = d;
- /* XXX The stats do not belong to here (text API), but oh well... */
- rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
+ rc = snprintf(name, NAMESZ, "%du", busnum);
+ if (rc <= 0 || rc >= NAMESZ)
+ goto err_print_u;
+ d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u);
+ if (d == NULL)
+ goto err_create_u;
+ mbus->dent_u = d;
+
+ rc = snprintf(name, NAMESZ, "%ds", busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_s;
d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat);
@@ -478,6 +690,10 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
err_create_s:
err_print_s:
+ debugfs_remove(mbus->dent_u);
+ mbus->dent_u = NULL;
+err_create_u:
+err_print_u:
debugfs_remove(mbus->dent_t);
mbus->dent_t = NULL;
err_create_t:
@@ -487,6 +703,7 @@ err_print_t:
void mon_text_del(struct mon_bus *mbus)
{
+ debugfs_remove(mbus->dent_u);
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
}
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
index efdfd8993d9e..13d63255283e 100644
--- a/drivers/usb/mon/usb_mon.h
+++ b/drivers/usb/mon/usb_mon.h
@@ -22,7 +22,7 @@ struct mon_bus {
int text_inited;
struct dentry *dent_s; /* Debugging file */
struct dentry *dent_t; /* Text interface file */
- int uses_dma;
+ struct dentry *dent_u; /* Second text interface file */
/* Ref */
int nreaders; /* Under mon_lock AND mbus->lock */
@@ -52,7 +52,7 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
struct mon_bus *mon_bus_lookup(unsigned int num);
-int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus);
+int /*bool*/ mon_text_add(struct mon_bus *mbus, int busnum);
void mon_text_del(struct mon_bus *mbus);
// void mon_bin_add(struct mon_bus *);
@@ -81,4 +81,6 @@ extern struct mutex mon_lock;
extern const struct file_operations mon_fops_stat;
+extern struct mon_bus mon_bus0; /* Only for redundant checks */
+
#endif /* __USB_MON_H */
diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c
index 5808ea082459..d5ef97bc4d01 100644
--- a/drivers/usb/net/asix.c
+++ b/drivers/usb/net/asix.c
@@ -298,7 +298,7 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
if (ax_skb) {
ax_skb->len = size;
ax_skb->data = packet;
- ax_skb->tail = packet + size;
+ skb_set_tail_pointer(ax_skb, size);
usbnet_skb_return(dev, ax_skb);
} else {
return 0;
@@ -338,7 +338,7 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *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;
+ skb_set_tail_pointer(skb, skb->len);
}
} else {
struct sk_buff *skb2;
@@ -352,11 +352,11 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
skb_push(skb, 4);
packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
cpu_to_le32s(&packet_len);
- memcpy(skb->data, &packet_len, sizeof(packet_len));
+ skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len));
if ((skb->len % 512) == 0) {
cpu_to_le32s(&padbytes);
- memcpy( skb->tail, &padbytes, sizeof(padbytes));
+ memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes));
skb_put(skb, sizeof(padbytes));
}
return skb;
diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c
index 4852012735f6..86e90c59d551 100644
--- a/drivers/usb/net/catc.c
+++ b/drivers/usb/net/catc.c
@@ -255,7 +255,6 @@ static void catc_rx_done(struct urb *urb)
if (!(skb = dev_alloc_skb(pkt_len)))
return;
- skb->dev = catc->netdev;
eth_copy_and_sum(skb, pkt_start + pkt_offset, pkt_len, 0);
skb_put(skb, pkt_len);
@@ -356,7 +355,7 @@ resubmit:
* Transmit routines.
*/
-static void catc_tx_run(struct catc *catc)
+static int catc_tx_run(struct catc *catc)
{
int status;
@@ -374,12 +373,14 @@ static void catc_tx_run(struct catc *catc)
catc->tx_ptr = 0;
catc->netdev->trans_start = jiffies;
+ return status;
}
static void catc_tx_done(struct urb *urb)
{
struct catc *catc = urb->context;
unsigned long flags;
+ int r;
if (urb->status == -ECONNRESET) {
dbg("Tx Reset.");
@@ -398,10 +399,13 @@ static void catc_tx_done(struct urb *urb)
spin_lock_irqsave(&catc->tx_lock, flags);
- if (catc->tx_ptr)
- catc_tx_run(catc);
- else
+ if (catc->tx_ptr) {
+ r = catc_tx_run(catc);
+ if (unlikely(r < 0))
+ clear_bit(TX_RUNNING, &catc->flags);
+ } else {
clear_bit(TX_RUNNING, &catc->flags);
+ }
netif_wake_queue(catc->netdev);
@@ -412,6 +416,7 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct catc *catc = netdev_priv(netdev);
unsigned long flags;
+ int r = 0;
char *tx_buf;
spin_lock_irqsave(&catc->tx_lock, flags);
@@ -419,11 +424,14 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6;
tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr;
*((u16*)tx_buf) = (catc->is_f5u011) ? cpu_to_be16((u16)skb->len) : cpu_to_le16((u16)skb->len);
- memcpy(tx_buf + 2, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, tx_buf + 2, skb->len);
catc->tx_ptr += skb->len + 2;
- if (!test_and_set_bit(TX_RUNNING, &catc->flags))
- catc_tx_run(catc);
+ if (!test_and_set_bit(TX_RUNNING, &catc->flags)) {
+ r = catc_tx_run(catc);
+ if (r < 0)
+ clear_bit(TX_RUNNING, &catc->flags);
+ }
if ((catc->is_f5u011 && catc->tx_ptr)
|| (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2))))
@@ -431,8 +439,10 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
spin_unlock_irqrestore(&catc->tx_lock, flags);
- catc->stats.tx_bytes += skb->len;
- catc->stats.tx_packets++;
+ if (r >= 0) {
+ catc->stats.tx_bytes += skb->len;
+ catc->stats.tx_packets++;
+ }
dev_kfree_skb(skb);
diff --git a/drivers/usb/net/dm9601.c b/drivers/usb/net/dm9601.c
index 5130cc7eb314..a67638601477 100644
--- a/drivers/usb/net/dm9601.c
+++ b/drivers/usb/net/dm9601.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/sched.h>
+#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -85,7 +86,7 @@ static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
usb_sndctrlpipe(dev->udev, 0),
DM_WRITE_REG,
USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
- value, reg, 0, 0, USB_CTRL_SET_TIMEOUT);
+ value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT);
}
static void dm_write_async_callback(struct urb *urb)
@@ -171,7 +172,7 @@ static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
usb_fill_control_urb(urb, dev->udev,
usb_sndctrlpipe(dev->udev, 0),
- (void *)req, 0, 0, dm_write_async_callback, req);
+ (void *)req, NULL, 0, dm_write_async_callback, req);
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status < 0) {
diff --git a/drivers/usb/net/gl620a.c b/drivers/usb/net/gl620a.c
index d257a8e026d6..031cf5ca4dbb 100644
--- a/drivers/usb/net/gl620a.c
+++ b/drivers/usb/net/gl620a.c
@@ -157,7 +157,7 @@ genelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
if ((headroom < (4 + 4*1)) || (tailroom < padlen)) {
skb->data = memmove(skb->head + (4 + 4*1),
skb->data, skb->len);
- skb->tail = skb->data + skb->len;
+ skb_set_tail_pointer(skb, skb->len);
}
} else {
struct sk_buff *skb2;
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
index de95268ae4b8..a0cc05d21a6a 100644
--- a/drivers/usb/net/kaweth.c
+++ b/drivers/usb/net/kaweth.c
@@ -636,8 +636,6 @@ static void kaweth_usb_receive(struct urb *urb)
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- skb->dev = net;
-
eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0);
skb_put(skb, pkt_len);
diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c
index ccebfdef4751..19bf8dae70c9 100644
--- a/drivers/usb/net/net1080.c
+++ b/drivers/usb/net/net1080.c
@@ -520,7 +520,7 @@ net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
skb->data = memmove(skb->head
+ sizeof (struct nc_header),
skb->data, skb->len);
- skb->tail = skb->data + len;
+ skb_set_tail_pointer(skb, len);
goto encapsulate;
}
}
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index d48c024cff59..a05fd97e5bc2 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -316,6 +316,7 @@ static int update_eth_regs_async(pegasus_t * pegasus)
return ret;
}
+/* Returns 0 on success, error on failure */
static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
{
int i;
@@ -574,7 +575,6 @@ static void fill_skb_pool(pegasus_t * pegasus)
*/
if (pegasus->rx_pool[i] == NULL)
return;
- pegasus->rx_pool[i]->dev = pegasus->net;
skb_reserve(pegasus->rx_pool[i], 2);
}
}
@@ -847,10 +847,6 @@ static void intr_callback(struct urb *urb)
* d[0].NO_CARRIER kicks in only with failed TX.
* ... so monitoring with MII may be safest.
*/
- if (d[0] & NO_CARRIER)
- netif_carrier_off(net);
- else
- netif_carrier_on(net);
/* bytes 3-4 == rx_lostpkt, reg 2E/2F */
pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
@@ -883,7 +879,7 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
netif_stop_queue(net);
((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16);
- memcpy(pegasus->tx_buff + 2, skb->data, skb->len);
+ skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len);
usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb,
usb_sndbulkpipe(pegasus->usb, 2),
pegasus->tx_buff, count,
@@ -950,7 +946,7 @@ static void set_carrier(struct net_device *net)
pegasus_t *pegasus = netdev_priv(net);
u16 tmp;
- if (!read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp))
+ if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp))
return;
if (tmp & BMSR_LSTATUS)
@@ -1408,8 +1404,10 @@ static void pegasus_disconnect(struct usb_interface *intf)
unlink_all_urbs(pegasus);
free_all_urbs(pegasus);
free_skb_pool(pegasus);
- if (pegasus->rx_skb)
+ if (pegasus->rx_skb != NULL) {
dev_kfree_skb(pegasus->rx_skb);
+ pegasus->rx_skb = NULL;
+ }
free_netdev(pegasus->net);
}
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c
index 39a21c74fdf4..980e4aaa97aa 100644
--- a/drivers/usb/net/rndis_host.c
+++ b/drivers/usb/net/rndis_host.c
@@ -253,6 +253,7 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */
* of that mess as possible.
*/
#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
+#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106)
#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e)
/*
@@ -349,7 +350,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
case RNDIS_MSG_INDICATE: { /* fault */
// struct rndis_indicate *msg = (void *)buf;
dev_info(&info->control->dev,
- "rndis fault indication\n");
+ "rndis fault indication\n");
}
break;
case RNDIS_MSG_KEEPALIVE: { /* ping */
@@ -387,6 +388,71 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
return -ETIMEDOUT;
}
+/*
+ * rndis_query:
+ *
+ * Performs a query for @oid along with 0 or more bytes of payload as
+ * specified by @in_len. If @reply_len is not set to -1 then the reply
+ * length is checked against this value, resulting in an error if it
+ * doesn't match.
+ *
+ * NOTE: Adding a payload exactly or greater than the size of the expected
+ * response payload is an evident requirement MSFT added for ActiveSync.
+ *
+ * The only exception is for OIDs that return a variably sized response,
+ * in which case no payload should be added. This undocumented (and
+ * nonsensical!) issue was found by sniffing protocol requests from the
+ * ActiveSync 4.1 Windows driver.
+ */
+static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
+ void *buf, u32 oid, u32 in_len,
+ void **reply, int *reply_len)
+{
+ int retval;
+ union {
+ void *buf;
+ struct rndis_msg_hdr *header;
+ struct rndis_query *get;
+ struct rndis_query_c *get_c;
+ } u;
+ u32 off, len;
+
+ u.buf = buf;
+
+ memset(u.get, 0, sizeof *u.get + in_len);
+ u.get->msg_type = RNDIS_MSG_QUERY;
+ u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
+ u.get->oid = oid;
+ u.get->len = cpu_to_le32(in_len);
+ u.get->offset = ccpu2(20);
+
+ retval = rndis_command(dev, u.header);
+ if (unlikely(retval < 0)) {
+ dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
+ oid, retval);
+ return retval;
+ }
+
+ off = le32_to_cpu(u.get_c->offset);
+ len = le32_to_cpu(u.get_c->len);
+ if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
+ goto response_error;
+
+ if (*reply_len != -1 && len != *reply_len)
+ goto response_error;
+
+ *reply = (unsigned char *) &u.get_c->request_id + off;
+ *reply_len = len;
+
+ return retval;
+
+response_error:
+ dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
+ "invalid response - off %d len %d\n",
+ oid, off, len);
+ return -EDOM;
+}
+
static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
{
int retval;
@@ -403,6 +469,8 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
struct rndis_set_c *set_c;
} u;
u32 tmp;
+ int reply_len;
+ unsigned char *bp;
/* we can't rely on i/o from stack working, or stack allocation */
u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
@@ -421,6 +489,12 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
* TX we'll stick to one Ethernet packet plus RNDIS framing.
* For RX we handle drivers that zero-pad to end-of-packet.
* Don't let userspace change these settings.
+ *
+ * NOTE: there still seems to be wierdness here, as if we need
+ * to do some more things to make sure WinCE targets accept this.
+ * They default to jumbograms of 8KB or 16KB, which is absurd
+ * for such low data rates and which is also more than Linux
+ * can usually expect to allocate for SKB data...
*/
net->hard_header_len += sizeof (struct rndis_data_hdr);
dev->hard_mtu = net->mtu + net->hard_header_len;
@@ -434,7 +508,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
if (unlikely(retval < 0)) {
/* it might not even be an RNDIS device!! */
dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
- goto fail_and_release;
+ goto fail_and_release;
}
tmp = le32_to_cpu(u.init_c->max_transfer_size);
if (tmp < dev->hard_mtu) {
@@ -450,34 +524,15 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
dev->hard_mtu, tmp, dev->rx_urb_size,
1 << le32_to_cpu(u.init_c->packet_alignment));
- /* Get designated host ethernet address.
- *
- * Adding a payload exactly the same size as the expected response
- * payload is an evident requirement MSFT added for ActiveSync.
- * This undocumented (and nonsensical) issue was found by sniffing
- * protocol requests from the ActiveSync 4.1 Windows driver.
- */
- memset(u.get, 0, sizeof *u.get + 48);
- u.get->msg_type = RNDIS_MSG_QUERY;
- u.get->msg_len = ccpu2(sizeof *u.get + 48);
- u.get->oid = OID_802_3_PERMANENT_ADDRESS;
- u.get->len = ccpu2(48);
- u.get->offset = ccpu2(20);
-
- retval = rndis_command(dev, u.header);
- if (unlikely(retval < 0)) {
+ /* Get designated host ethernet address */
+ reply_len = ETH_ALEN;
+ retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
+ 48, (void **) &bp, &reply_len);
+ if (unlikely(retval< 0)) {
dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
goto fail_and_release;
}
- tmp = le32_to_cpu(u.get_c->offset);
- if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN)
- || u.get_c->len != ccpu2(ETH_ALEN))) {
- dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n",
- tmp, le32_to_cpu(u.get_c->len));
- retval = -EDOM;
- goto fail_and_release;
- }
- memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN);
+ memcpy(net->dev_addr, bp, ETH_ALEN);
/* set a nonzero filter to enable data transfers */
memset(u.set, 0, sizeof *u.set);
@@ -502,6 +557,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
fail_and_release:
usb_set_intfdata(info->data, NULL);
usb_driver_release_interface(driver_of(intf), info->data);
+ info->data = NULL;
fail:
kfree(u.buf);
return retval;
@@ -588,7 +644,7 @@ rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
if (likely((sizeof *hdr) <= room)) {
skb->data = memmove(skb->head + sizeof *hdr,
skb->data, len);
- skb->tail = skb->data + len;
+ skb_set_tail_pointer(skb, len);
goto fill;
}
}
@@ -618,7 +674,7 @@ fill:
static const struct driver_info rndis_info = {
.description = "RNDIS device",
- .flags = FLAG_ETHER | FLAG_FRAMING_RN,
+ .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
.bind = rndis_bind,
.unbind = rndis_unbind,
.status = rndis_status,
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index ea153dc9b0ac..fa598f0340cf 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -646,7 +646,6 @@ static void fill_skb_pool(rtl8150_t *dev)
if (!skb) {
return;
}
- skb->dev = dev->netdev;
skb_reserve(skb, 2);
dev->rx_skb_pool[i] = skb;
}
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
index de69b183bd2f..f9cd42d058b0 100644
--- a/drivers/usb/net/usbnet.c
+++ b/drivers/usb/net/usbnet.c
@@ -203,7 +203,6 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
{
int status;
- skb->dev = dev->net;
skb->protocol = eth_type_trans (skb, dev->net);
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
@@ -735,8 +734,7 @@ void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
{
struct usbnet *dev = netdev_priv(net);
- /* REVISIT don't always return "usbnet" */
- strncpy (info->driver, driver_name, sizeof info->driver);
+ strncpy (info->driver, dev->driver_name, sizeof info->driver);
strncpy (info->version, DRIVER_VERSION, sizeof info->version);
strncpy (info->fw_version, dev->driver_info->description,
sizeof info->fw_version);
@@ -1116,10 +1114,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
struct driver_info *info;
struct usb_device *xdev;
int status;
+ const char *name;
+ name = udev->dev.driver->name;
info = (struct driver_info *) prod->driver_info;
if (!info) {
- dev_dbg (&udev->dev, "blacklisted by %s\n", driver_name);
+ dev_dbg (&udev->dev, "blacklisted by %s\n", name);
return -ENODEV;
}
xdev = interface_to_usbdev (udev);
@@ -1139,6 +1139,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
dev = netdev_priv(net);
dev->udev = xdev;
dev->driver_info = info;
+ dev->driver_name = name;
dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
| NETIF_MSG_PROBE | NETIF_MSG_LINK);
skb_queue_head_init (&dev->rxq);
diff --git a/drivers/usb/net/usbnet.h b/drivers/usb/net/usbnet.h
index 07c70abbe0ec..cbb53e065d6c 100644
--- a/drivers/usb/net/usbnet.h
+++ b/drivers/usb/net/usbnet.h
@@ -29,6 +29,7 @@ struct usbnet {
/* housekeeping */
struct usb_device *udev;
struct driver_info *driver_info;
+ const char *driver_name;
wait_queue_head_t *wait;
struct mutex phy_mutex;
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 2f4d303ee36f..ba5d1dc03036 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -423,11 +423,11 @@ config USB_SERIAL_MCT_U232
module will be called mct_u232.
config USB_SERIAL_MOS7720
- tristate "USB Moschip 7720 Single Port Serial Driver"
+ tristate "USB Moschip 7720 Serial Driver"
depends on USB_SERIAL
---help---
- Say Y here if you want to use a USB Serial single port adapter from
- Moschip Semiconductor Tech.
+ Say Y here if you want to use USB Serial single and double
+ port adapters from Moschip Semiconductor Tech.
To compile this driver as a module, choose M here: the
module will be called mos7720.
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
index 11dad42c3c60..b675735bfbee 100644
--- a/drivers/usb/serial/aircable.c
+++ b/drivers/usb/serial/aircable.c
@@ -209,6 +209,7 @@ 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;
+ u16 *dbuf;
dbg("%s - port %d", __FUNCTION__, port->number);
if (port->write_urb_busy)
return;
@@ -226,8 +227,8 @@ static void aircable_send(struct usb_serial_port *port)
buf[0] = TX_HEADER_0;
buf[1] = TX_HEADER_1;
- buf[2] = (unsigned char)count;
- buf[3] = (unsigned char)(count >> 8);
+ dbuf = (u16 *)&buf[2];
+ *dbuf = cpu_to_le16((u16)count);
serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE);
memcpy(port->write_urb->transfer_buffer, buf,
@@ -434,7 +435,7 @@ static void aircable_write_bulk_callback(struct urb *urb)
__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);
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev,
"%s - failed resubmitting write urb, error %d\n",
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index edd685791a6b..ea2175bb2274 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -341,7 +341,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
result = usb_serial_generic_open(port, filp);
if (result)
- return result;
+ goto err_out;
/* open */
ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf);
@@ -372,6 +372,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
if (port->tty)
ark3116_set_termios(port, &tmp_termios);
+err_out:
kfree(buf);
return result;
diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
index d7d0ba986a80..e831cb7f64fd 100644
--- a/drivers/usb/serial/cp2101.c
+++ b/drivers/usb/serial/cp2101.c
@@ -58,9 +58,11 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
+ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
{ USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+ { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */
{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
{ USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
{ USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8ff9d54b21e6..95a1805b064f 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -342,6 +342,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
@@ -1433,6 +1434,7 @@ static int ftdi_write (struct usb_serial_port *port,
dbg("%s - write limit hit\n", __FUNCTION__);
return 0;
}
+ priv->tx_outstanding_urbs++;
spin_unlock_irqrestore(&priv->tx_lock, flags);
data_offset = priv->write_offset;
@@ -1450,14 +1452,15 @@ static int ftdi_write (struct usb_serial_port *port,
buffer = kmalloc (transfer_size, GFP_ATOMIC);
if (!buffer) {
err("%s ran out of kernel memory for urb ...", __FUNCTION__);
- return -ENOMEM;
+ count = -ENOMEM;
+ goto error_no_buffer;
}
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
err("%s - no more free urbs", __FUNCTION__);
- kfree (buffer);
- return -ENOMEM;
+ count = -ENOMEM;
+ goto error_no_urb;
}
/* Copy data */
@@ -1499,10 +1502,9 @@ static int ftdi_write (struct usb_serial_port *port,
if (status) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
count = status;
- kfree (buffer);
+ goto error;
} else {
spin_lock_irqsave(&priv->tx_lock, flags);
- ++priv->tx_outstanding_urbs;
priv->tx_outstanding_bytes += count;
priv->tx_bytes += count;
spin_unlock_irqrestore(&priv->tx_lock, flags);
@@ -1510,10 +1512,19 @@ static int ftdi_write (struct usb_serial_port *port,
/* we are done with this urb, so let the host driver
* really free it when it is finished with it */
- usb_free_urb (urb);
+ usb_free_urb(urb);
dbg("%s write returning: %d", __FUNCTION__, count);
return count;
+error:
+ usb_free_urb(urb);
+error_no_urb:
+ kfree (buffer);
+error_no_buffer:
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ priv->tx_outstanding_urbs--;
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+ return count;
} /* ftdi_write */
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 513cfe1b768b..77ad0a09b384 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -31,6 +31,7 @@
#define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */
#define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */
#define FTDI_NF_RIC_PID 0x0001 /* Product Id */
+#define FTDI_USBX_707_PID 0xF857 /* ADSTech IR Blaster USBX-707 */
/* www.canusb.com Lawicel CANUSB device */
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 6a26a2e683a6..18f74ac76565 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -111,7 +111,7 @@ struct edgeport_port {
struct TxFifo txfifo; /* transmit fifo -- size will be maxTxCredits */
struct urb *write_urb; /* write URB for this port */
- char write_in_progress; /* TRUE while a write URB is outstanding */
+ bool write_in_progress; /* 'true' while a write URB is outstanding */
spinlock_t ep_lock;
__u8 shadowLCR; /* last LCR value received */
@@ -123,11 +123,11 @@ struct edgeport_port {
__u8 validDataMask;
__u32 baudRate;
- char open;
- char openPending;
- char commandPending;
- char closePending;
- char chaseResponsePending;
+ bool open;
+ bool openPending;
+ bool commandPending;
+ bool closePending;
+ bool chaseResponsePending;
wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */
wait_queue_head_t wait_open; /* for handling sleeping while waiting for open to finish */
@@ -156,7 +156,7 @@ struct edgeport_serial {
__u8 bulk_in_endpoint; /* the bulk in endpoint handle */
unsigned char * bulk_in_buffer; /* the buffer we use for the bulk in endpoint */
struct urb * read_urb; /* our bulk read urb */
- int read_in_progress;
+ bool read_in_progress;
spinlock_t es_lock;
__u8 bulk_out_endpoint; /* the bulk out endpoint handle */
@@ -212,7 +212,7 @@ static int debug;
static int low_latency = 1; /* tty low latency flag, on by default */
-static int CmdUrbs = 0; /* Number of outstanding Command Write Urbs */
+static atomic_t CmdUrbs; /* Number of outstanding Command Write Urbs */
/* local function prototypes */
@@ -631,14 +631,14 @@ static void edge_interrupt_callback (struct urb *urb)
if (edge_serial->rxBytesAvail > 0 &&
!edge_serial->read_in_progress) {
dbg("%s - posting a read", __FUNCTION__);
- edge_serial->read_in_progress = TRUE;
+ edge_serial->read_in_progress = true;
/* we have pending bytes on the bulk in pipe, send a request */
edge_serial->read_urb->dev = edge_serial->serial->dev;
result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
if (result) {
dev_err(&edge_serial->serial->dev->dev, "%s - usb_submit_urb(read bulk) failed with result = %d\n", __FUNCTION__, result);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
}
}
spin_unlock(&edge_serial->es_lock);
@@ -695,13 +695,13 @@ static void edge_bulk_in_callback (struct urb *urb)
if (urb->status) {
dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
return;
}
if (urb->actual_length == 0) {
dbg("%s - read bulk callback with no data", __FUNCTION__);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
return;
}
@@ -725,10 +725,10 @@ static void edge_bulk_in_callback (struct urb *urb)
status = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
if (status) {
dev_err(&urb->dev->dev, "%s - usb_submit_urb(read bulk) failed, status = %d\n", __FUNCTION__, status);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
}
} else {
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
}
spin_unlock(&edge_serial->es_lock);
@@ -759,7 +759,7 @@ static void edge_bulk_out_data_callback (struct urb *urb)
}
// Release the Write URB
- edge_port->write_in_progress = FALSE;
+ edge_port->write_in_progress = false;
// Check if more data needs to be sent
send_more_port_data((struct edgeport_serial *)(usb_get_serial_data(edge_port->port->serial)), edge_port);
@@ -779,8 +779,8 @@ static void edge_bulk_out_cmd_callback (struct urb *urb)
dbg("%s", __FUNCTION__);
- CmdUrbs--;
- dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs);
+ atomic_dec(&CmdUrbs);
+ dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs));
/* clean up the transfer buffer */
@@ -802,7 +802,7 @@ static void edge_bulk_out_cmd_callback (struct urb *urb)
tty_wakeup(tty);
/* we have completed the command */
- edge_port->commandPending = FALSE;
+ edge_port->commandPending = false;
wake_up(&edge_port->wait_command);
}
@@ -868,7 +868,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
port0->bulk_in_buffer,
edge_serial->read_urb->transfer_buffer_length,
edge_bulk_in_callback, edge_serial);
- edge_serial->read_in_progress = FALSE;
+ edge_serial->read_in_progress = false;
/* start interrupt read for this edgeport
* this interrupt will continue as long as the edgeport is connected */
@@ -890,26 +890,26 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
/* initialize our port settings */
edge_port->txCredits = 0; /* Can't send any data yet */
edge_port->shadowMCR = MCR_MASTER_IE; /* Must always set this bit to enable ints! */
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
/* send a open port command */
- edge_port->openPending = TRUE;
- edge_port->open = FALSE;
+ edge_port->openPending = true;
+ edge_port->open = false;
response = send_iosp_ext_cmd (edge_port, IOSP_CMD_OPEN_PORT, 0);
if (response < 0) {
dev_err(&port->dev, "%s - error sending open port command\n", __FUNCTION__);
- edge_port->openPending = FALSE;
+ edge_port->openPending = false;
return -ENODEV;
}
/* now wait for the port to be completely opened */
- wait_event_timeout(edge_port->wait_open, (edge_port->openPending != TRUE), OPEN_TIMEOUT);
+ wait_event_timeout(edge_port->wait_open, !edge_port->openPending, OPEN_TIMEOUT);
- if (edge_port->open == FALSE) {
+ if (!edge_port->open) {
/* open timed out */
dbg("%s - open timedout", __FUNCTION__);
- edge_port->openPending = FALSE;
+ edge_port->openPending = false;
return -ENODEV;
}
@@ -928,7 +928,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
/* Allocate a URB for the write */
edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL);
- edge_port->write_in_progress = FALSE;
+ edge_port->write_in_progress = false;
if (!edge_port->write_urb) {
dbg("%s - no memory", __FUNCTION__);
@@ -966,7 +966,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port)
lastCredits = edge_port->txCredits;
// Did we get our Chase response
- if (edge_port->chaseResponsePending == FALSE) {
+ if (!edge_port->chaseResponsePending) {
dbg("%s - Got Chase Response", __FUNCTION__);
// did we get all of our credit back?
@@ -985,7 +985,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port)
// No activity.. count down.
loop--;
if (loop == 0) {
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
dbg("%s - Chase TIMEOUT", __FUNCTION__);
return;
}
@@ -1068,13 +1068,13 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
// block until tx is empty
block_until_tx_empty(edge_port);
- edge_port->closePending = TRUE;
+ edge_port->closePending = true;
if ((!edge_serial->is_epic) ||
((edge_serial->is_epic) &&
(edge_serial->epic_descriptor.Supports.IOSPChase))) {
/* flush and chase */
- edge_port->chaseResponsePending = TRUE;
+ edge_port->chaseResponsePending = true;
dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
@@ -1082,7 +1082,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
// block until chase finished
block_until_chase_response(edge_port);
} else {
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
}
}
@@ -1094,10 +1094,10 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0);
}
- //port->close = TRUE;
- edge_port->closePending = FALSE;
- edge_port->open = FALSE;
- edge_port->openPending = FALSE;
+ //port->close = true;
+ edge_port->closePending = false;
+ edge_port->open = false;
+ edge_port->openPending = false;
usb_kill_urb(edge_port->write_urb);
@@ -1247,7 +1247,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge
}
// lock this write
- edge_port->write_in_progress = TRUE;
+ edge_port->write_in_progress = true;
// get a pointer to the write_urb
urb = edge_port->write_urb;
@@ -1261,7 +1261,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge
buffer = kmalloc (count+2, GFP_ATOMIC);
if (buffer == NULL) {
dev_err(&edge_port->port->dev, "%s - no more kernel memory...\n", __FUNCTION__);
- edge_port->write_in_progress = FALSE;
+ edge_port->write_in_progress = false;
goto exit_send;
}
buffer[0] = IOSP_BUILD_DATA_HDR1 (edge_port->port->number - edge_port->port->serial->minor, count);
@@ -1301,7 +1301,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge
if (status) {
/* something went wrong */
dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", __FUNCTION__, status);
- edge_port->write_in_progress = FALSE;
+ edge_port->write_in_progress = false;
/* revert the credits as something bad happened. */
edge_port->txCredits += count;
@@ -1332,7 +1332,7 @@ static int edge_write_room (struct usb_serial_port *port)
if (edge_port == NULL)
return -ENODEV;
- if (edge_port->closePending == TRUE)
+ if (edge_port->closePending)
return -ENODEV;
dbg("%s - port %d", __FUNCTION__, port->number);
@@ -1371,7 +1371,7 @@ static int edge_chars_in_buffer (struct usb_serial_port *port)
if (edge_port == NULL)
return -ENODEV;
- if (edge_port->closePending == TRUE)
+ if (edge_port->closePending)
return -ENODEV;
if (!edge_port->open) {
@@ -1762,7 +1762,7 @@ static void edge_break (struct usb_serial_port *port, int break_state)
((edge_serial->is_epic) &&
(edge_serial->epic_descriptor.Supports.IOSPChase))) {
/* flush and chase */
- edge_port->chaseResponsePending = TRUE;
+ edge_port->chaseResponsePending = true;
dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
@@ -1770,7 +1770,7 @@ static void edge_break (struct usb_serial_port *port, int break_state)
// block until chase finished
block_until_chase_response(edge_port);
} else {
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
}
}
@@ -1952,13 +1952,13 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2
// Also, we currently clear flag and close the port regardless of content of above's Byte3.
// We could choose to do something else when Byte3 says Timeout on Chase from Edgeport,
// like wait longer in block_until_chase_response, but for now we don't.
- edge_port->chaseResponsePending = FALSE;
+ edge_port->chaseResponsePending = false;
wake_up (&edge_port->wait_chase);
return;
case IOSP_EXT_STATUS_RX_CHECK_RSP:
dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============\n", __FUNCTION__, edge_serial->rxPort, byte3 );
- //Port->RxCheckRsp = TRUE;
+ //Port->RxCheckRsp = true;
return;
}
}
@@ -1974,8 +1974,8 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2
change_port_settings (edge_port, edge_port->port->tty->termios);
/* we have completed the open */
- edge_port->openPending = FALSE;
- edge_port->open = TRUE;
+ edge_port->openPending = false;
+ edge_port->open = true;
wake_up(&edge_port->wait_open);
return;
}
@@ -1983,7 +1983,7 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2
// If port is closed, silently discard all rcvd status. We can
// have cases where buffered status is received AFTER the close
// port command is sent to the Edgeport.
- if ((!edge_port->open ) || (edge_port->closePending)) {
+ if (!edge_port->open || edge_port->closePending) {
return;
}
@@ -1991,14 +1991,14 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2
// Not currently sent by Edgeport
case IOSP_STATUS_LSR:
dbg("%s - Port %u LSR Status = %02x", __FUNCTION__, edge_serial->rxPort, byte2);
- handle_new_lsr (edge_port, FALSE, byte2, 0);
+ handle_new_lsr(edge_port, false, byte2, 0);
break;
case IOSP_STATUS_LSR_DATA:
dbg("%s - Port %u LSR Status = %02x, Data = %02x", __FUNCTION__, edge_serial->rxPort, byte2, byte3);
// byte2 is LSR Register
// byte3 is broken data byte
- handle_new_lsr (edge_port, TRUE, byte2, byte3);
+ handle_new_lsr(edge_port, true, byte2, byte3);
break;
//
// case IOSP_EXT_4_STATUS:
@@ -2317,14 +2317,14 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer
if (!urb)
return -ENOMEM;
- CmdUrbs++;
- dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs);
+ atomic_inc(&CmdUrbs);
+ dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs));
usb_fill_bulk_urb (urb, edge_serial->serial->dev,
usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint),
buffer, length, edge_bulk_out_cmd_callback, edge_port);
- edge_port->commandPending = TRUE;
+ edge_port->commandPending = true;
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
@@ -2332,16 +2332,16 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer
dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __FUNCTION__, status);
usb_kill_urb(urb);
usb_free_urb(urb);
- CmdUrbs--;
+ atomic_dec(&CmdUrbs);
return status;
}
// wait for command to finish
timeout = COMMAND_TIMEOUT;
#if 0
- wait_event (&edge_port->wait_command, (edge_port->commandPending == FALSE));
+ wait_event (&edge_port->wait_command, !edge_port->commandPending);
- if (edge_port->commandPending == TRUE) {
+ if (edge_port->commandPending) {
/* command timed out */
dbg("%s - command timed out", __FUNCTION__);
status = -EINVAL;
@@ -2524,8 +2524,8 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi
dbg("%s - port %d", __FUNCTION__, edge_port->port->number);
- if ((!edge_port->open) &&
- (!edge_port->openPending)) {
+ if (!edge_port->open &&
+ !edge_port->openPending) {
dbg("%s - port not opened", __FUNCTION__);
return;
}
@@ -2836,9 +2836,9 @@ static int edge_startup (struct usb_serial *serial)
struct usb_device *dev;
int i, j;
int response;
- int interrupt_in_found;
- int bulk_in_found;
- int bulk_out_found;
+ bool interrupt_in_found;
+ bool bulk_in_found;
+ bool bulk_out_found;
static __u32 descriptor[3] = { EDGE_COMPATIBILITY_MASK0,
EDGE_COMPATIBILITY_MASK1,
EDGE_COMPATIBILITY_MASK2 };
@@ -2936,14 +2936,14 @@ static int edge_startup (struct usb_serial *serial)
if (edge_serial->is_epic) {
/* EPIC thing, set up our interrupt polling now and our read urb, so
* that the device knows it really is connected. */
- interrupt_in_found = bulk_in_found = bulk_out_found = FALSE;
+ interrupt_in_found = bulk_in_found = bulk_out_found = false;
for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) {
struct usb_endpoint_descriptor *endpoint;
int buffer_size;
endpoint = &serial->interface->altsetting[0].endpoint[i].desc;
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
- if ((!interrupt_in_found) &&
+ if (!interrupt_in_found &&
(usb_endpoint_is_int_in(endpoint))) {
/* we found a interrupt in endpoint */
dbg("found interrupt in");
@@ -2972,10 +2972,10 @@ static int edge_startup (struct usb_serial *serial)
edge_serial,
endpoint->bInterval);
- interrupt_in_found = TRUE;
+ interrupt_in_found = true;
}
- if ((!bulk_in_found) &&
+ if (!bulk_in_found &&
(usb_endpoint_is_bulk_in(endpoint))) {
/* we found a bulk in endpoint */
dbg("found bulk in");
@@ -3001,19 +3001,19 @@ static int edge_startup (struct usb_serial *serial)
endpoint->wMaxPacketSize,
edge_bulk_in_callback,
edge_serial);
- bulk_in_found = TRUE;
+ bulk_in_found = true;
}
- if ((!bulk_out_found) &&
+ if (!bulk_out_found &&
(usb_endpoint_is_bulk_out(endpoint))) {
/* we found a bulk out endpoint */
dbg("found bulk out");
edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress;
- bulk_out_found = TRUE;
+ bulk_out_found = true;
}
}
- if ((!interrupt_in_found) || (!bulk_in_found) || (!bulk_out_found)) {
+ if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) {
err ("Error - the proper endpoints were not found!");
return -ENODEV;
}
@@ -3083,6 +3083,7 @@ static int __init edgeport_init(void)
retval = usb_register(&io_driver);
if (retval)
goto failed_usb_register;
+ atomic_set(&CmdUrbs, 0);
info(DRIVER_DESC " " DRIVER_VERSION);
return 0;
diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h
index 29a913a6daca..cb201c1f67f9 100644
--- a/drivers/usb/serial/io_edgeport.h
+++ b/drivers/usb/serial/io_edgeport.h
@@ -19,12 +19,6 @@
#define MAX_RS232_PORTS 8 /* Max # of RS-232 ports per device */
/* typedefs that the insideout headers need */
-#ifndef TRUE
- #define TRUE (1)
-#endif
-#ifndef FALSE
- #define FALSE (0)
-#endif
#ifndef LOW8
#define LOW8(a) ((unsigned char)(a & 0xff))
#endif
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index d16e2e1764ad..4df0ec74e0b1 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -255,6 +255,7 @@ static struct usb_device_id ipaq_id_table [] = {
{ USB_DEVICE(0x04DD, 0x9102) }, /* SHARP WS003SH USB Modem */
{ USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */
{ USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */
+ { USB_DEVICE(0x04DD, 0x9151) }, /* SHARP S01SH USB Modem */
{ USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */
{ USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index b2097c45a235..7b085f334ceb 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -238,7 +238,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
if (rc < 0)
err("Reading line status failed (error = %d)", rc);
else {
- status = status_buf[0] + (status_buf[1]<<8);
+ status = le16_to_cpu(*(u16 *)status_buf);
info("%s - read status %x %x", __FUNCTION__,
status_buf[0], status_buf[1]);
@@ -257,7 +257,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
static int klsi_105_startup (struct usb_serial *serial)
{
struct klsi_105_private *priv;
- int i;
+ int i, j;
/* check if we support the product id (see keyspan.c)
* FIXME
@@ -265,12 +265,12 @@ static int klsi_105_startup (struct usb_serial *serial)
/* allocate the private data structure */
for (i=0; i<serial->num_ports; i++) {
- int j;
priv = kmalloc(sizeof(struct klsi_105_private),
GFP_KERNEL);
if (!priv) {
dbg("%skmalloc for klsi_105_private failed.", __FUNCTION__);
- return -ENOMEM;
+ i--;
+ goto err_cleanup;
}
/* set initial values for control structures */
priv->cfg.pktlen = 5;
@@ -292,15 +292,14 @@ static int klsi_105_startup (struct usb_serial *serial)
priv->write_urb_pool[j] = urb;
if (urb == NULL) {
err("No more urbs???");
- continue;
+ goto err_cleanup;
}
- 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;
+ goto err_cleanup;
}
}
@@ -308,7 +307,20 @@ static int klsi_105_startup (struct usb_serial *serial)
init_waitqueue_head(&serial->port[i]->write_wait);
}
- return (0);
+ return 0;
+
+err_cleanup:
+ for (; i >= 0; i--) {
+ priv = usb_get_serial_port_data(serial->port[i]);
+ for (j=0; j < NUM_URBS; j++) {
+ if (priv->write_urb_pool[j]) {
+ kfree(priv->write_urb_pool[j]->transfer_buffer);
+ usb_free_urb(priv->write_urb_pool[j]);
+ }
+ }
+ usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ return -ENOMEM;
} /* klsi_105_startup */
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 4cd839b1407f..3db1adc25f84 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -438,17 +438,21 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp)
if (retval) {
err("usb_submit_urb(read bulk) failed pipe 0x%x err %d",
port->read_urb->pipe, retval);
- goto exit;
+ goto error;
}
port->interrupt_in_urb->dev = port->serial->dev;
retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
- if (retval)
+ if (retval) {
+ usb_kill_urb(port->read_urb);
err(" usb_submit_urb(read int) failed pipe 0x%x err %d",
port->interrupt_in_urb->pipe, retval);
-
-exit:
+ goto error;
+ }
return 0;
+
+error:
+ return retval;
} /* mct_u232_open */
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 19bf403f9db2..b563e2ad8728 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -103,11 +103,9 @@ static void mos7720_interrupt_callback(struct urb *urb)
{
int result;
int length;
- __u32 *data;
- unsigned int status;
+ __u8 *data;
__u8 sp1;
__u8 sp2;
- __u8 st;
dbg("%s"," : Entering\n");
@@ -141,18 +139,19 @@ static void mos7720_interrupt_callback(struct urb *urb)
* Byte 2 IIR Port 2 (port.number is 1)
* Byte 3 --------------
* Byte 4 FIFO status for both */
- if (length && length > 4) {
+
+ /* the above description is inverted
+ * oneukum 2007-03-14 */
+
+ if (unlikely(length != 4)) {
dbg("Wrong data !!!");
return;
}
- status = *data;
-
- sp1 = (status & 0xff000000)>>24;
- sp2 = (status & 0x00ff0000)>>16;
- st = status & 0x000000ff;
+ sp1 = data[3];
+ sp2 = data[2];
- if ((sp1 & 0x01) || (sp2 & 0x01)) {
+ if ((sp1 | sp2) & 0x01) {
/* No Interrupt Pending in both the ports */
dbg("No Interrupt !!!");
} else {
@@ -333,6 +332,7 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp)
int response;
int port_number;
char data;
+ int allocated_urbs = 0;
int j;
serial = port->serial;
@@ -353,7 +353,7 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp)
/* Initialising the write urb pool */
for (j = 0; j < NUM_URBS; ++j) {
- urb = usb_alloc_urb(0,GFP_ATOMIC);
+ urb = usb_alloc_urb(0,GFP_KERNEL);
mos7720_port->write_urb_pool[j] = urb;
if (urb == NULL) {
@@ -365,10 +365,16 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp)
GFP_KERNEL);
if (!urb->transfer_buffer) {
err("%s-out of memory for urb buffers.", __FUNCTION__);
+ usb_free_urb(mos7720_port->write_urb_pool[j]);
+ mos7720_port->write_urb_pool[j] = NULL;
continue;
}
+ allocated_urbs++;
}
+ if (!allocated_urbs)
+ return -ENOMEM;
+
/* Initialize MCS7720 -- Write Init values to corresponding Registers
*
* Register Index
@@ -526,7 +532,7 @@ static int mos7720_chars_in_buffer(struct usb_serial_port *port)
}
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
+ if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
chars += URB_TRANSFER_BUFFER_SIZE;
}
dbg("%s - returns %d", __FUNCTION__, chars);
@@ -629,7 +635,7 @@ static int mos7720_write_room(struct usb_serial_port *port)
}
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
+ if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
room += URB_TRANSFER_BUFFER_SIZE;
}
@@ -664,7 +670,7 @@ static int mos7720_write(struct usb_serial_port *port,
urb = NULL;
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
urb = mos7720_port->write_urb_pool[i];
dbg("URB:%d",i);
break;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index c6cca859af45..2366e7b63ece 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -176,9 +176,12 @@ 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 */
+ struct urb *int_urb;
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
char open;
+ char open_ports;
+ char zombie;
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;
@@ -191,17 +194,17 @@ struct moschip_port {
__u8 DcrRegOffset;
//for processing control URBS in interrupt context
struct urb *control_urb;
+ struct usb_ctrlrequest *dr;
char *ctrl_buf;
int MsrLsr;
+ spinlock_t pool_lock;
struct urb *write_urb_pool[NUM_URBS];
+ char busy[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
@@ -254,7 +257,7 @@ static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
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) {
+ if (port->serial->num_ports == 4) {
val |=
(((__u16) port->number - (__u16) (port->serial->minor)) +
1) << 8;
@@ -294,7 +297,7 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
//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) {
+ if (port->serial->num_ports == 4) {
Wval =
(((__u16) port->number - (__u16) (port->serial->minor)) +
1) << 8;
@@ -352,7 +355,7 @@ static inline struct moschip_port *mos7840_get_port_private(struct
return (struct moschip_port *)usb_get_serial_port_data(port);
}
-static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
+static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
{
struct moschip_port *mos7840_port;
struct async_icount *icount;
@@ -366,22 +369,24 @@ static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
/* update input line counters */
if (new_msr & MOS_MSR_DELTA_CTS) {
icount->cts++;
+ smp_wmb();
}
if (new_msr & MOS_MSR_DELTA_DSR) {
icount->dsr++;
+ smp_wmb();
}
if (new_msr & MOS_MSR_DELTA_CD) {
icount->dcd++;
+ smp_wmb();
}
if (new_msr & MOS_MSR_DELTA_RI) {
icount->rng++;
+ smp_wmb();
}
}
-
- return 0;
}
-static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
+static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
{
struct async_icount *icount;
@@ -400,18 +405,20 @@ static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
icount = &port->icount;
if (new_lsr & SERIAL_LSR_BI) {
icount->brk++;
+ smp_wmb();
}
if (new_lsr & SERIAL_LSR_OE) {
icount->overrun++;
+ smp_wmb();
}
if (new_lsr & SERIAL_LSR_PE) {
icount->parity++;
+ smp_wmb();
}
if (new_lsr & SERIAL_LSR_FE) {
icount->frame++;
+ smp_wmb();
}
-
- return 0;
}
/************************************************************************/
@@ -426,12 +433,15 @@ static void mos7840_control_callback(struct urb *urb)
unsigned char *data;
struct moschip_port *mos7840_port;
__u8 regval = 0x0;
+ int result = 0;
if (!urb) {
dbg("%s", "Invalid Pointer !!!!:\n");
return;
}
+ mos7840_port = (struct moschip_port *)urb->context;
+
switch (urb->status) {
case 0:
/* success */
@@ -449,8 +459,6 @@ static void mos7840_control_callback(struct urb *urb)
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);
@@ -462,21 +470,26 @@ static void mos7840_control_callback(struct urb *urb)
else if (mos7840_port->MsrLsr == 1)
mos7840_handle_new_lsr(mos7840_port, regval);
- exit:
- return;
+exit:
+ spin_lock(&mos7840_port->pool_lock);
+ if (!mos7840_port->zombie)
+ result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC);
+ spin_unlock(&mos7840_port->pool_lock);
+ if (result) {
+ dev_err(&urb->dev->dev,
+ "%s - Error %d submitting interrupt urb\n",
+ __FUNCTION__, result);
+ }
}
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;
+ struct usb_ctrlrequest *dr = mcs->dr;
+ unsigned char *buffer = mcs->ctrl_buf;
+ int ret;
-// 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;
@@ -506,8 +519,8 @@ static void mos7840_interrupt_callback(struct urb *urb)
__u16 Data;
unsigned char *data;
__u8 sp[5], st;
- int i;
- __u16 wval;
+ int i, rv = 0;
+ __u16 wval, wreg = 0;
dbg("%s", " : Entering\n");
if (!urb) {
@@ -569,31 +582,34 @@ static void mos7840_interrupt_callback(struct urb *urb)
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);
+ wreg = LINE_STATUS_REGISTER;
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);
+ wreg = MODEM_STATUS_REGISTER;
break;
}
+ spin_lock(&mos7840_port->pool_lock);
+ if (!mos7840_port->zombie) {
+ rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data);
+ } else {
+ spin_unlock(&mos7840_port->pool_lock);
+ return;
+ }
+ spin_unlock(&mos7840_port->pool_lock);
}
}
}
- exit:
+ if (!(rv < 0)) /* the completion handler for the control urb will resubmit */
+ return;
+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,
@@ -634,7 +650,8 @@ static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port,
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 */
+ /* then say that we don't have a valid usb_serial thing, which will
+ * end up genrating -ENODEV return values */
return NULL;
}
@@ -699,6 +716,7 @@ static void mos7840_bulk_in_callback(struct urb *urb)
tty_flip_buffer_push(tty);
}
mos7840_port->icount.rx += urb->actual_length;
+ smp_wmb();
dbg("mos7840_port->icount.rx is %d:\n",
mos7840_port->icount.rx);
}
@@ -708,15 +726,14 @@ static void mos7840_bulk_in_callback(struct urb *urb)
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);
+ mos7840_port->read_urb->dev = serial->dev;
- if (status) {
- dbg(" usb_submit_urb(read bulk) failed, status = %d",
- status);
- }
+ status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+ if (status) {
+ dbg(" usb_submit_urb(read bulk) failed, status = %d",
+ status);
}
}
@@ -730,17 +747,28 @@ static void mos7840_bulk_out_data_callback(struct urb *urb)
{
struct moschip_port *mos7840_port;
struct tty_struct *tty;
+ int i;
+
if (!urb) {
dbg("%s", "Invalid Pointer !!!!:\n");
return;
}
+ mos7840_port = (struct moschip_port *)urb->context;
+ spin_lock(&mos7840_port->pool_lock);
+ for (i = 0; i < NUM_URBS; i++) {
+ if (urb == mos7840_port->write_urb_pool[i]) {
+ mos7840_port->busy[i] = 0;
+ break;
+ }
+ }
+ spin_unlock(&mos7840_port->pool_lock);
+
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;
@@ -792,13 +820,13 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
__u16 Data;
int status;
struct moschip_port *mos7840_port;
+ struct moschip_port *port0;
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__)) {
@@ -807,16 +835,18 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
mos7840_port = mos7840_get_port_private(port);
+ port0 = mos7840_get_port_private(serial->port[0]);
- if (mos7840_port == NULL)
+ if (mos7840_port == NULL || port0 == NULL)
return -ENODEV;
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
+ port0->open_ports++;
/* Initialising the write urb pool */
for (j = 0; j < NUM_URBS; ++j) {
- urb = usb_alloc_urb(0, GFP_ATOMIC);
+ urb = usb_alloc_urb(0, GFP_KERNEL);
mos7840_port->write_urb_pool[j] = urb;
if (urb == NULL) {
@@ -824,10 +854,10 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
continue;
}
- urb->transfer_buffer = NULL;
- urb->transfer_buffer =
- kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+ urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
if (!urb->transfer_buffer) {
+ usb_free_urb(urb);
+ mos7840_port->write_urb_pool[j] = NULL;
err("%s-out of memory for urb buffers.", __FUNCTION__);
continue;
}
@@ -879,9 +909,7 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
Data |= 0x08; //Driver done bit
Data |= 0x20; //rx_disable
- status = 0;
- status =
- mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+ status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
if (status < 0) {
dbg("writing Controlreg failed\n");
return -1;
@@ -893,7 +921,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
////////////////////////////////////
Data = 0x00;
- status = 0;
status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
if (status < 0) {
dbg("disableing interrupts failed\n");
@@ -901,7 +928,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
// 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");
@@ -909,7 +935,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
Data = 0xcf;
- status = 0;
status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
if (status < 0) {
dbg("Writing FIFO_CONTROL_REGISTER failed\n");
@@ -917,22 +942,18 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
}
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;
@@ -999,7 +1020,7 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp)
/* 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 (port0->open_ports == 1) {
if (serial->port[0]->interrupt_in_buffer == NULL) {
/* set up interrupt urb */
@@ -1097,6 +1118,7 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port)
{
int i;
int chars = 0;
+ unsigned long flags;
struct moschip_port *mos7840_port;
dbg("%s \n", " mos7840_chars_in_buffer:entering ...........");
@@ -1112,13 +1134,15 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port)
return -1;
}
+ spin_lock_irqsave(&mos7840_port->pool_lock,flags);
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) {
+ if (mos7840_port->busy[i]) {
chars += URB_TRANSFER_BUFFER_SIZE;
}
}
+ spin_unlock_irqrestore(&mos7840_port->pool_lock,flags);
dbg("%s - returns %d", __FUNCTION__, chars);
- return (chars);
+ return chars;
}
@@ -1172,6 +1196,7 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial;
struct moschip_port *mos7840_port;
+ struct moschip_port *port0;
int j;
__u16 Data;
@@ -1189,10 +1214,10 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp)
}
mos7840_port = mos7840_get_port_private(port);
+ port0 = mos7840_get_port_private(serial->port[0]);
- if (mos7840_port == NULL) {
+ if (mos7840_port == NULL || port0 == NULL)
return;
- }
for (j = 0; j < NUM_URBS; ++j)
usb_kill_urb(mos7840_port->write_urb_pool[j]);
@@ -1234,12 +1259,13 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp)
}
// if(mos7840_port->ctrl_buf != NULL)
// kfree(mos7840_port->ctrl_buf);
- mos7840_num_open_ports--;
+ port0->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) {
+ port0->open_ports, port->number);
+ if (port0->open_ports == 0) {
if (serial->port[0]->interrupt_in_urb) {
dbg("%s", "Shutdown interrupt_in_urb\n");
+ usb_kill_urb(serial->port[0]->interrupt_in_urb);
}
}
@@ -1368,6 +1394,7 @@ static int mos7840_write_room(struct usb_serial_port *port)
{
int i;
int room = 0;
+ unsigned long flags;
struct moschip_port *mos7840_port;
dbg("%s \n", " mos7840_write_room:entering ...........");
@@ -1384,14 +1411,17 @@ static int mos7840_write_room(struct usb_serial_port *port)
return -1;
}
+ spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ if (!mos7840_port->busy[i]) {
room += URB_TRANSFER_BUFFER_SIZE;
}
}
+ spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
+ room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1;
dbg("%s - returns %d", __FUNCTION__, room);
- return (room);
+ return room;
}
@@ -1410,6 +1440,7 @@ static int mos7840_write(struct usb_serial_port *port,
int i;
int bytes_sent = 0;
int transfer_size;
+ unsigned long flags;
struct moschip_port *mos7840_port;
struct usb_serial *serial;
@@ -1476,13 +1507,16 @@ static int mos7840_write(struct usb_serial_port *port,
/* try to find a free urb in the list */
urb = NULL;
+ spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
- if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ if (!mos7840_port->busy[i]) {
+ mos7840_port->busy[i] = 1;
urb = mos7840_port->write_urb_pool[i];
dbg("\nURB:%d", i);
break;
}
}
+ spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
if (urb == NULL) {
dbg("%s - no more free urbs", __FUNCTION__);
@@ -1518,6 +1552,7 @@ static int mos7840_write(struct usb_serial_port *port,
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
+ mos7840_port->busy[i] = 0;
err("%s - usb_submit_urb(write bulk) failed with status = %d",
__FUNCTION__, status);
bytes_sent = status;
@@ -1525,6 +1560,7 @@ static int mos7840_write(struct usb_serial_port *port,
}
bytes_sent = transfer_size;
mos7840_port->icount.tx += transfer_size;
+ smp_wmb();
dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx);
exit:
@@ -2490,6 +2526,7 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
if (signal_pending(current))
return -ERESTARTSYS;
cnow = mos7840_port->icount;
+ smp_rmb();
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; /* no change => error */
@@ -2506,6 +2543,7 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
case TIOCGICOUNT:
cnow = mos7840_port->icount;
+ smp_rmb();
icount.cts = cnow.cts;
icount.dsr = cnow.dsr;
icount.rng = cnow.rng;
@@ -2535,19 +2573,18 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
static int mos7840_calc_num_ports(struct usb_serial *serial)
{
+ int mos7840_num_ports = 0;
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;
+ mos7840_num_ports = serial->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;
+ serial->num_bulk_in = 4;
+ serial->num_bulk_out = 4;
+ mos7840_num_ports = serial->num_ports = 4;
}
return mos7840_num_ports;
@@ -2583,7 +2620,9 @@ static int mos7840_startup(struct usb_serial *serial)
mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
if (mos7840_port == NULL) {
err("%s - Out of memory", __FUNCTION__);
- return -ENOMEM;
+ status = -ENOMEM;
+ i--; /* don't follow NULL pointer cleaning up */
+ goto error;
}
/* Initialize all port interrupt end point to port 0 int endpoint *
@@ -2591,6 +2630,7 @@ static int mos7840_startup(struct usb_serial *serial)
mos7840_port->port = serial->port[i];
mos7840_set_port_private(serial->port[i], mos7840_port);
+ spin_lock_init(&mos7840_port->pool_lock);
mos7840_port->port_num = ((serial->port[i]->number -
(serial->port[i]->serial->minor)) +
@@ -2601,22 +2641,22 @@ static int mos7840_startup(struct usb_serial *serial)
mos7840_port->ControlRegOffset = 0x1;
mos7840_port->DcrRegOffset = 0x4;
} else if ((mos7840_port->port_num == 2)
- && (mos7840_num_ports == 4)) {
+ && (serial->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)) {
+ && (serial->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)) {
+ && (serial->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)) {
+ && (serial->num_ports == 4)) {
mos7840_port->SpRegOffset = 0xc;
mos7840_port->ControlRegOffset = 0xd;
mos7840_port->DcrRegOffset = 0x1c;
@@ -2701,21 +2741,19 @@ static int mos7840_startup(struct usb_serial *serial)
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;
+ goto error;
} 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);
@@ -2729,7 +2767,7 @@ static int mos7840_startup(struct usb_serial *serial)
//Zero Length flag register
if ((mos7840_port->port_num != 1)
- && (mos7840_num_ports == 2)) {
+ && (serial->num_ports == 2)) {
Data = 0xff;
status = 0;
@@ -2770,14 +2808,17 @@ static int mos7840_startup(struct usb_serial *serial)
i + 1, status);
}
- mos7840_port->control_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL);
mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
-
+ mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf || !mos7840_port->dr) {
+ status = -ENOMEM;
+ goto error;
+ }
}
//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);
@@ -2789,6 +2830,17 @@ static int mos7840_startup(struct usb_serial *serial)
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
(__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ);
return 0;
+error:
+ for (/* nothing */; i >= 0; i--) {
+ mos7840_port = mos7840_get_port_private(serial->port[i]);
+
+ kfree(mos7840_port->dr);
+ kfree(mos7840_port->ctrl_buf);
+ usb_free_urb(mos7840_port->control_urb);
+ kfree(mos7840_port);
+ serial->port[i] = NULL;
+ }
+ return status;
}
/****************************************************************************
@@ -2799,6 +2851,7 @@ static int mos7840_startup(struct usb_serial *serial)
static void mos7840_shutdown(struct usb_serial *serial)
{
int i;
+ unsigned long flags;
struct moschip_port *mos7840_port;
dbg("%s \n", " shutdown :entering..........");
@@ -2814,8 +2867,12 @@ static void mos7840_shutdown(struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
mos7840_port = mos7840_get_port_private(serial->port[i]);
- kfree(mos7840_port->ctrl_buf);
+ spin_lock_irqsave(&mos7840_port->pool_lock, flags);
+ mos7840_port->zombie = 1;
+ spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
usb_kill_urb(mos7840_port->control_urb);
+ kfree(mos7840_port->ctrl_buf);
+ kfree(mos7840_port->dr);
kfree(mos7840_port);
mos7840_set_port_private(serial->port[i], NULL);
}
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 0216ac12a27d..4adfab988e86 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -69,6 +69,7 @@ static void omninet_write_bulk_callback (struct urb *urb);
static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count);
static int omninet_write_room (struct usb_serial_port *port);
static void omninet_shutdown (struct usb_serial *serial);
+static int omninet_attach (struct usb_serial *serial);
static struct usb_device_id id_table [] = {
{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
@@ -99,6 +100,7 @@ static struct usb_serial_driver zyxel_omninet_device = {
.num_bulk_in = 1,
.num_bulk_out = 2,
.num_ports = 1,
+ .attach = omninet_attach,
.open = omninet_open,
.close = omninet_close,
.write = omninet_write,
@@ -145,22 +147,30 @@ struct omninet_data
__u8 od_outseq; // Sequence number for bulk_out URBs
};
+static int omninet_attach (struct usb_serial *serial)
+{
+ struct omninet_data *od;
+ struct usb_serial_port *port = serial->port[0];
+
+ od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
+ if( !od ) {
+ err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data));
+ return -ENOMEM;
+ }
+ usb_set_serial_port_data(port, od);
+ return 0;
+}
+
static int omninet_open (struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial = port->serial;
struct usb_serial_port *wport;
- struct omninet_data *od;
+ struct omninet_data *od = usb_get_serial_port_data(port);
int result = 0;
dbg("%s - port %d", __FUNCTION__, port->number);
od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
- if( !od ) {
- err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data));
- return -ENOMEM;
- }
-
- usb_set_serial_port_data(port, od);
wport = serial->port[1];
wport->tty = port->tty;
@@ -170,24 +180,17 @@ static int omninet_open (struct usb_serial_port *port, struct file *filp)
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
omninet_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_KERNEL);
- if (result)
+ if (result) {
err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+ }
return result;
}
static void omninet_close (struct usb_serial_port *port, struct file * filp)
{
- struct usb_serial *serial = port->serial;
- struct usb_serial_port *wport;
-
dbg("%s - port %d", __FUNCTION__, port->number);
-
- wport = serial->port[1];
- usb_kill_urb(wport->write_urb);
usb_kill_urb(port->read_urb);
-
- kfree(usb_get_serial_port_data(port));
}
@@ -326,7 +329,12 @@ static void omninet_write_bulk_callback (struct urb *urb)
static void omninet_shutdown (struct usb_serial *serial)
{
+ struct usb_serial_port *wport = serial->port[1];
+ struct usb_serial_port *port = serial->port[0];
dbg ("%s", __FUNCTION__);
+
+ usb_kill_urb(wport->write_urb);
+ kfree(usb_get_serial_port_data(port));
}
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index e178e6f40319..8c3f55b080b4 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -113,6 +113,12 @@ static int option_send_setup(struct usb_serial_port *port);
#define ANYDATA_VENDOR_ID 0x16d5
#define ANYDATA_PRODUCT_ID 0x6501
+#define BANDRICH_VENDOR_ID 0x1A8D
+#define BANDRICH_PRODUCT_C100_1 0x1002
+#define BANDRICH_PRODUCT_C100_2 0x1003
+
+#define DELL_VENDOR_ID 0x413C
+
static struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -165,6 +171,9 @@ static struct usb_device_id option_ids[] = {
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2130) }, /* Novatel Merlin ES620 SM Bus */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2410) }, /* Novatel EU740 */
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
+ { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
+ { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
+ { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
@@ -591,12 +600,6 @@ static int option_open(struct usb_serial_port *port, struct file *filp)
return (0);
}
-static inline void stop_urb(struct urb *urb)
-{
- if (urb && urb->status == -EINPROGRESS)
- usb_kill_urb(urb);
-}
-
static void option_close(struct usb_serial_port *port, struct file *filp)
{
int i;
@@ -614,9 +617,9 @@ static void option_close(struct usb_serial_port *port, struct file *filp)
/* Stop reading/writing urbs */
for (i = 0; i < N_IN_URB; i++)
- stop_urb(portdata->in_urbs[i]);
+ usb_kill_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
- stop_urb(portdata->out_urbs[i]);
+ usb_kill_urb(portdata->out_urbs[i]);
}
port->tty = NULL;
}
@@ -747,9 +750,9 @@ static void option_shutdown(struct usb_serial *serial)
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
for (j = 0; j < N_IN_URB; j++)
- stop_urb(portdata->in_urbs[j]);
+ usb_kill_urb(portdata->in_urbs[j]);
for (j = 0; j < N_OUT_URB; j++)
- stop_urb(portdata->out_urbs[j]);
+ usb_kill_urb(portdata->out_urbs[j]);
}
/* Now free them */
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index ecedd833818d..644607de4c11 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -456,12 +456,6 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp)
return (0);
}
-static inline void stop_urb(struct urb *urb)
-{
- if (urb && urb->status == -EINPROGRESS)
- usb_kill_urb(urb);
-}
-
static void sierra_close(struct usb_serial_port *port, struct file *filp)
{
int i;
@@ -479,9 +473,9 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp)
/* Stop reading/writing urbs */
for (i = 0; i < N_IN_URB; i++)
- stop_urb(portdata->in_urbs[i]);
+ usb_unlink_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
- stop_urb(portdata->out_urbs[i]);
+ usb_unlink_urb(portdata->out_urbs[i]);
}
port->tty = NULL;
}
@@ -583,17 +577,26 @@ static void sierra_shutdown(struct usb_serial *serial)
/* Stop reading/writing urbs */
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
+ if (!port)
+ continue;
portdata = usb_get_serial_port_data(port);
+ if (!portdata)
+ continue;
+
for (j = 0; j < N_IN_URB; j++)
- stop_urb(portdata->in_urbs[j]);
+ usb_unlink_urb(portdata->in_urbs[j]);
for (j = 0; j < N_OUT_URB; j++)
- stop_urb(portdata->out_urbs[j]);
+ usb_unlink_urb(portdata->out_urbs[j]);
}
/* Now free them */
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
+ if (!port)
+ continue;
portdata = usb_get_serial_port_data(port);
+ if (!portdata)
+ continue;
for (j = 0; j < N_IN_URB; j++) {
if (portdata->in_urbs[j]) {
@@ -612,6 +615,8 @@ static void sierra_shutdown(struct usb_serial *serial)
/* Now free per port private data */
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
+ if (!port)
+ continue;
kfree(usb_get_serial_port_data(port));
}
}
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 2f59ff226e2c..ffbe601cde2a 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -384,19 +384,21 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf,
dbg("%s - write limit hit\n", __FUNCTION__);
return 0;
}
+ priv->outstanding_urbs++;
spin_unlock_irqrestore(&priv->lock, flags);
buffer = kmalloc (count, GFP_ATOMIC);
if (!buffer) {
dev_err(&port->dev, "out of memory\n");
- return -ENOMEM;
+ count = -ENOMEM;
+ goto error_no_buffer;
}
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
dev_err(&port->dev, "no more free urbs\n");
- kfree (buffer);
- return -ENOMEM;
+ count = -ENOMEM;
+ goto error_no_urb;
}
memcpy (buffer, buf, count);
@@ -415,19 +417,27 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf,
dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n",
__FUNCTION__, status);
count = status;
- kfree (buffer);
+ goto error;
} else {
spin_lock_irqsave(&priv->lock, flags);
- ++priv->outstanding_urbs;
priv->bytes_out += count;
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);
+ usb_free_urb(urb);
return count;
+error:
+ usb_free_urb(urb);
+error_no_urb:
+ kfree(buffer);
+error_no_buffer:
+ spin_lock_irqsave(&priv->lock, flags);
+ --priv->outstanding_urbs;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return count;
}
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index bf16e9e1d84e..27c5f8f9a2d5 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -1109,7 +1109,7 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *
command_port = port->serial->port[COMMAND_PORT];
command_info = usb_get_serial_port_data(command_port);
spin_lock_irqsave(&command_info->lock, flags);
- command_info->command_finished = FALSE;
+ command_info->command_finished = false;
transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
transfer_buffer[0] = command;
@@ -1124,12 +1124,12 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *
spin_unlock_irqrestore(&command_info->lock, flags);
/* wait for the command to complete */
- wait_event_interruptible_timeout(command_info->wait_command,
- (command_info->command_finished != FALSE), COMMAND_TIMEOUT);
+ wait_event_interruptible_timeout(command_info->wait_command,
+ (bool)command_info->command_finished, COMMAND_TIMEOUT);
spin_lock_irqsave(&command_info->lock, flags);
- if (command_info->command_finished == FALSE) {
+ if (command_info->command_finished == false) {
dbg("%s - command timed out.", __FUNCTION__);
retval = -ETIMEDOUT;
goto exit;
diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h
index d714eff58dc0..f16079705664 100644
--- a/drivers/usb/serial/whiteheat.h
+++ b/drivers/usb/serial/whiteheat.h
@@ -20,10 +20,6 @@
#define __LINUX_USB_SERIAL_WHITEHEAT_H
-#define FALSE 0
-#define TRUE 1
-
-
/* WhiteHEAT commands */
#define WHITEHEAT_OPEN 1 /* open the port */
#define WHITEHEAT_CLOSE 2 /* close the port */
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
index 599ad10a761b..06d1107dbd47 100644
--- a/drivers/usb/storage/libusual.c
+++ b/drivers/usb/storage/libusual.c
@@ -117,6 +117,7 @@ EXPORT_SYMBOL_GPL(usb_usual_check_type);
static int usu_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
+ int rc;
unsigned long type;
struct task_struct* task;
unsigned long flags;
@@ -135,7 +136,7 @@ static int usu_probe(struct usb_interface *intf,
task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type);
if (IS_ERR(task)) {
- int rc = PTR_ERR(task);
+ rc = PTR_ERR(task);
printk(KERN_WARNING "libusual: "
"Unable to start the thread for %s: %d\n",
bias_names[type], rc);
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 4a9d0d5c7282..8b3145ab7757 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1371,15 +1371,6 @@ UNUSUAL_DEV( 0x1210, 0x0003, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
-/* This prevents the kernel from detecting the virtual cd-drive with the
- * Windows drivers. <johann.wilhelm@student.tugraz.at>
-*/
-UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0xffff,
- "HUAWEI",
- "E220 USB-UMTS Install",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_IGNORE_DEVICE),
-
/* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */
UNUSUAL_DEV( 0x132b, 0x000b, 0x0001, 0x0001,
"Minolta",
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index 46929a1b6f24..8432bf171d2e 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -34,18 +34,25 @@ static struct usb_device_id skel_table [] = {
};
MODULE_DEVICE_TABLE(usb, skel_table);
+/* to prevent a race between open and disconnect */
+static DEFINE_MUTEX(skel_open_lock);
+
/* 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)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+ allocations > PAGE_SIZE and the number of packets in a page
+ is an integer 512 is the largest possible packet on EHCI */
#define WRITES_IN_FLIGHT 8
+/* arbitrarily chosen */
/* Structure to hold all of our device specific stuff */
struct usb_skel {
- struct usb_device *dev; /* the usb device for this device */
- struct usb_interface *interface; /* the interface for this device */
+ struct usb_device *udev; /* 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 */
size_t bulk_in_size; /* the size of the receive buffer */
@@ -76,8 +83,10 @@ static int skel_open(struct inode *inode, struct file *file)
subminor = iminor(inode);
+ mutex_lock(&skel_open_lock);
interface = usb_find_interface(&skel_driver, subminor);
if (!interface) {
+ mutex_unlock(&skel_open_lock);
err ("%s - error, can't find device for minor %d",
__FUNCTION__, subminor);
retval = -ENODEV;
@@ -86,12 +95,15 @@ static int skel_open(struct inode *inode, struct file *file)
dev = usb_get_intfdata(interface);
if (!dev) {
+ mutex_unlock(&skel_open_lock);
retval = -ENODEV;
goto exit;
}
/* increment our usage count for the device */
kref_get(&dev->kref);
+ /* now we can drop the lock */
+ mutex_unlock(&skel_open_lock);
/* prevent the device from being autosuspended */
retval = usb_autopm_get_interface(interface);
@@ -201,12 +213,6 @@ 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) {
@@ -225,6 +231,14 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
goto error;
}
+ /* this lock makes sure we don't submit URBs to gone devices */
+ mutex_lock(&dev->io_mutex);
+ if (!dev->interface) { /* disconnect() was called */
+ mutex_unlock(&dev->io_mutex);
+ retval = -ENODEV;
+ goto error;
+ }
+
/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
@@ -233,6 +247,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
+ mutex_unlock(&dev->io_mutex);
if (retval) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error;
@@ -241,7 +256,7 @@ 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);
- mutex_unlock(&dev->io_mutex);
+
return writesize;
error:
@@ -249,7 +264,6 @@ error:
usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
- mutex_unlock(&dev->io_mutex);
up(&dev->limit_sem);
exit:
@@ -344,6 +358,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
error:
if (dev)
+ /* this frees allocated memory */
kref_put(&dev->kref, skel_delete);
return retval;
}
@@ -354,20 +369,21 @@ static void skel_disconnect(struct usb_interface *interface)
int minor = interface->minor;
/* prevent skel_open() from racing skel_disconnect() */
- lock_kernel();
+ mutex_lock(&skel_open_lock);
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
/* give back our minor */
usb_deregister_dev(interface, &skel_class);
+ mutex_unlock(&skel_open_lock);
/* 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 */
kref_put(&dev->kref, skel_delete);
@@ -380,6 +396,7 @@ static struct usb_driver skel_driver = {
.probe = skel_probe,
.disconnect = skel_disconnect,
.id_table = skel_table,
+ .supports_autosuspend = 1,
};
static int __init usb_skel_init(void)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 5af684b40496..b1cb72c3780f 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -139,7 +139,7 @@ config FB_TILEBLITTING
This is particularly important to one driver, matroxfb. If
unsure, say N.
-comment "Frambuffer hardware drivers"
+comment "Frame buffer hardware drivers"
depends on FB
config FB_CIRRUS
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index d7627fc4f11e..8514f2a6f060 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -2899,7 +2899,7 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev,
struct fb_info *info, unsigned long addr)
{
struct atyfb_par *par = info->par;
- struct pcidev_cookie *pcp;
+ struct device_node *dp;
char prop[128];
int node, len, i, j, ret;
u32 mem, chip_id;
@@ -3037,8 +3037,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev,
node = 0;
}
- pcp = pdev->sysdata;
- if (node == pcp->prom_node->node) {
+ dp = pci_device_to_OF_node(pdev);
+ if (node == dp->node) {
struct fb_var_screeninfo *var = &default_var;
unsigned int N, P, Q, M, T, R;
u32 v_total, h_total;
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
index 1bf6f42eb400..a4b3fd185de7 100644
--- a/drivers/video/aty/radeon_base.c
+++ b/drivers/video/aty/radeon_base.c
@@ -410,7 +410,7 @@ static int __devinit radeon_find_mem_vbios(struct radeonfb_info *rinfo)
}
#endif
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
/*
* Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
* tree. Hopefully, ATI OF driver is kind enough to fill these
@@ -440,7 +440,7 @@ static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo)
return 0;
}
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
/*
* Read PLL infos from chip registers
@@ -645,7 +645,7 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
/*
* Retrieve PLL infos from Open Firmware first
*/
@@ -653,7 +653,7 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
printk(KERN_INFO "radeonfb: Retrieved PLL infos from Open Firmware\n");
goto found;
}
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
/*
* Check out if we have an X86 which gave us some PLL informations
@@ -2231,7 +2231,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
rinfo->family == CHIP_FAMILY_RS200)
rinfo->errata |= CHIP_ERRATA_PLL_DELAY;
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
/* On PPC, we obtain the OF device-node pointer to the firmware
* data for this chip
*/
@@ -2240,6 +2240,8 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
printk(KERN_WARNING "radeonfb (%s): Cannot match card to OF node !\n",
pci_name(rinfo->pdev));
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
+#ifdef CONFIG_PPC_OF
/* On PPC, the firmware sets up a memory mapping that tends
* to cause lockups when enabling the engine. We reconfigure
* the card internal memory mappings properly
diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c
index 38c7dbf8c151..737b5c09dbdb 100644
--- a/drivers/video/aty/radeon_monitor.c
+++ b/drivers/video/aty/radeon_monitor.c
@@ -52,7 +52,7 @@ static char *radeon_get_mon_name(int type)
}
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
/*
* Try to find monitor informations & EDID data out of the Open Firmware
* device-tree. This also contains some "hacks" to work around a few machine
@@ -156,7 +156,7 @@ static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_
}
return MT_NONE;
}
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
@@ -495,11 +495,11 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
* Old single head cards
*/
if (!rinfo->has_CRTC2) {
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
if (rinfo->mon1_type == MT_NONE)
rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
&rinfo->mon1_EDID);
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
#ifdef CONFIG_FB_RADEON_I2C
if (rinfo->mon1_type == MT_NONE)
rinfo->mon1_type =
@@ -544,11 +544,11 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
/*
* Probe primary head (DVI or laptop internal panel)
*/
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
if (rinfo->mon1_type == MT_NONE)
rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
&rinfo->mon1_EDID);
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
#ifdef CONFIG_FB_RADEON_I2C
if (rinfo->mon1_type == MT_NONE)
rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi,
@@ -572,11 +572,11 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
/*
* Probe secondary head (mostly VGA, can be DVI)
*/
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
if (rinfo->mon2_type == MT_NONE)
rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1,
&rinfo->mon2_EDID);
-#endif /* CONFIG_PPC_OF */
+#endif /* CONFIG_PPC_OF || defined(CONFIG_SPARC) */
#ifdef CONFIG_FB_RADEON_I2C
if (rinfo->mon2_type == MT_NONE)
rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_vga,
diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h
index d5ff224a6258..319000360285 100644
--- a/drivers/video/aty/radeonfb.h
+++ b/drivers/video/aty/radeonfb.h
@@ -16,7 +16,7 @@
#include <asm/io.h>
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
#include <asm/prom.h>
#endif
@@ -292,7 +292,7 @@ struct radeonfb_info {
unsigned long fb_local_base;
struct pci_dev *pdev;
-#ifdef CONFIG_PPC_OF
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
struct device_node *of_node;
#endif
diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c
index 767c850f8eb7..f042428a84f4 100644
--- a/drivers/video/cg3.c
+++ b/drivers/video/cg3.c
@@ -266,7 +266,7 @@ static void __devinit cg3_init_fix(struct fb_info *info, int linebytes,
static void __devinit cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var,
struct device_node *dp)
{
- char *params;
+ const char *params;
char *p;
int ww, hh;
diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c
index 90592fb59156..eb1a4812ad1d 100644
--- a/drivers/video/igafb.c
+++ b/drivers/video/igafb.c
@@ -44,8 +44,8 @@
#include <asm/io.h>
-#ifdef __sparc__
-#include <asm/pbm.h>
+#ifdef CONFIG_SPARC
+#include <asm/prom.h>
#include <asm/pcic.h>
#endif
@@ -96,7 +96,7 @@ struct fb_var_screeninfo default_var = {
.vmode = FB_VMODE_NONINTERLACED
};
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
struct fb_var_screeninfo default_var_1024x768 __initdata = {
/* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
.xres = 1024,
@@ -188,7 +188,7 @@ static inline void iga_outb(struct iga_par *par, unsigned char val,
pci_outb(par, val, reg+1);
}
-#endif /* __sparc__ */
+#endif /* CONFIG_SPARC */
/*
* Very important functionality for the JavaEngine1 computer:
@@ -217,7 +217,7 @@ static void iga_blank_border(struct iga_par *par)
iga_outb(par, 0, IGA_EXT_CNTRL, IGA_IDX_OVERSCAN_COLOR + i);
}
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
static int igafb_mmap(struct fb_info *info,
struct vm_area_struct *vma)
{
@@ -271,7 +271,7 @@ static int igafb_mmap(struct fb_info *info,
vma->vm_flags |= VM_IO;
return 0;
}
-#endif /* __sparc__ */
+#endif /* CONFIG_SPARC */
static int igafb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
@@ -323,7 +323,7 @@ static struct fb_ops igafb_ops = {
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
.fb_mmap = igafb_mmap,
#endif
};
@@ -424,7 +424,7 @@ int __init igafb_init(void)
par->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK;
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
/*
* The following is sparc specific and this is why:
*
@@ -477,8 +477,8 @@ int __init igafb_init(void)
* Set default vmode and cmode from PROM properties.
*/
{
- struct pcidev_cookie *cookie = pdev->sysdata;
- int node = cookie->prom_node;
+ struct device_node *dp = pci_device_to_OF_node(pdev);
+ int node = dp->node;
int width = prom_getintdefault(node, "width", 1024);
int height = prom_getintdefault(node, "height", 768);
int depth = prom_getintdefault(node, "depth", 8);
@@ -534,7 +534,7 @@ int __init igafb_init(void)
kfree(info);
}
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
/*
* Add /dev/fb mmap values.
*/
@@ -552,7 +552,7 @@ int __init igafb_init(void)
par->mmap_map[1].size = PAGE_SIZE * 2; /* X wants 2 pages */
par->mmap_map[1].prot_mask = SRMMU_CACHE;
par->mmap_map[1].prot_flag = SRMMU_WRITE;
-#endif /* __sparc__ */
+#endif /* CONFIG_SPARC */
return 0;
}